Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>CfgNodeTraversalCallback gatherCb = new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { // n.getParent() isn't null. This just the case where n is the root // node that gatherCb started at. if (parent == null) { return; } // Make sure that the name node is purely a read. if ((NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || parent.isVar() || parent.isInc() || parent.isDec() || parent.isParamList() || parent.isCatch()) { return; } String name = n.getString(); if (compiler.getCodingConvention().isExported(name)) { return; } Definition def = reachingDef.getDef(name, cfgNode); // TODO(nicksantos): We need to add some notion of @const outer // scope vars. We can inline those just fine. if (def != null && !reachingDef.dependsOnOuterScopeVars(def)) { candidates.add(new Candidate(name, def, n, cfgNode)); } } } }; NodeTraversal.traverse(compiler, cfgNode, gatherCb); } } /** * Models the connection between a definition and a use of that definition. */ private class Candidate { // Name of the variable. private final String varName; // Nodes related to the definition. private Node def; getDefinition(getDefCfgNode(), null); getNumUseInUseCfgNode(useCfgNode, null); // Definition was not found. if (def == null) { return false; } // Check that the assignment isn't used as a R-Value. // TODO(user): Certain cases we can still inline. if (def.isAssign() && !NodeUtil.isExprAssign(def.getParent())) { return false; } // The right of the definition has side effect: // Example, for x: // x = readProp(b), modifyProp(b); print(x); if (checkRightOf(def, getDefCfgNode(), SIDE_

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>BetweenNodes<Node, ControlFlowGraph.Branch>( cfg, cfg.getDirectedGraphNode(getDefCfgNode()), cfg.getDirectedGraphNode(useCfgNode), SIDE_EFFECT_PREDICATE, Predicates. <DiGraphEdge<Node, ControlFlowGraph.Branch>>alwaysTrue(), false); if (pathCheck.somePathsSatisfyPredicate()) { return false; } } return true; } /** * Actual transformation. */ private void inlineVariable() { Node defParent = def.getParent(); Node useParent = use.getParent(); if (def.isAssign()) { Node rhs = def.getLastChild(); rhs.detachFromParent(); // Oh yes! I have grandparent to remove this. Preconditions.checkState(defParent.isExprResult()); while (defParent.getParent().isLabel()) { defParent = defParent.getParent(); } defParent.detachFromParent(); useParent.replaceChild(use, rhs); } else if (defParent.isVar()) { Node rhs = def.getLastChild(); def.removeChild(rhs); useParent.replaceChild(use, rhs); } else { Preconditions.checkState(false, "No other definitions can be inlined."); } compiler.reportCodeChange(); } /** * Set the def node * * @param n A node that has a corresponding CFG node in the CFG. */ private void getDefinition(Node n, Node parent) { AbstractCfgNodeTraversalCallback gatherCb = new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (n.getString().equals(varName) && n.hasChildren()) { def = n; } return; case Token.ASSIGN: Node lhs = n.getFirstChild(); if (lhs.isName() && lhs.getString().equals(varName)) { def = n; } return; } } }; NodeTraversal.traverse(compiler, n, gatherCb); } /** * Computes the number of uses of the variable varName and store it in * numUseWithinUse

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>CfgNode. */ private void getNumUseInUseCfgNode(Node n, Node parant) { AbstractCfgNodeTraversalCallback gatherCb = new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName() && n.getString().equals(varName) && // do not count in if it is left child of an assignment operator !(parent.isAssign() && (parent.getFirstChild() == n))) { numUseWithinUseCfgNode++; } } }; NodeTraversal.traverse(compiler, n, gatherCb); } } /** * Given an expression by its root and sub-expression n, return true if there * the predicate is true for some expression on the right of n. * * Example: * * NotChecked(), NotChecked(), n, Checked(), Checked(); */ private static boolean checkRightOf( Node n, Node expressionRoot, Predicate<Node> predicate) { for (Node p = n; p != expressionRoot; p = p.getParent()) { for (Node cur = p.getNext(); cur != null; cur = cur.getNext()) { if (predicate.apply(cur)) { return true; } } } return false; } /** * Given an expression by its root and sub-expression n, return true if there * the predicate is true for some expression on the left of n. * * Example: * * Checked(), Checked(), n, NotChecked(), NotChecked(); */ private static boolean checkLeftOf( Node n, Node expressionRoot, Predicate<Node> predicate) { for (Node p = n.getParent(); p != expressionRoot; p = p.getParent()) { for (Node cur = p.getParent().getFirstChild(); cur != p; cur = cur.getNext()) { if (predicate.apply(cur)) { return true; } } } return false; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set<Scope.Var> initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet<Scope.Var>(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var =

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; } case Token.INC: case Token.DEC: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { reportError(t, n, name); } } break; } } } /** * Gets whether a variable is a constant initialized to a literal value at * the point where it is declared. */ private boolean isConstant(Scope.Var var) { return var != null && var.isConst(); } /** * Reports a reassigned constant error. */ void reportError(NodeTraversal t, Node n, String name) { compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name)); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>CleaupPassFactory", false) { @Override protected HotSwapCompilerPass createInternal( AbstractCompiler compiler) { return new FieldCleanupPass(compiler); } }; final PassFactory scopeCleanupPassFactory = new HotSwapPassFactory("ScopeCleanupPassFactory", false) { @Override protected HotSwapCompilerPass createInternal( AbstractCompiler compiler) { return new MemoizedScopeCleanupPass(compiler); } }; final PassFactory globalVarRefCleanupPassFactory = new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) { @Override protected HotSwapCompilerPass createInternal( AbstractCompiler compiler) { return new GlobalVarRefCleanupPass(compiler); } }; /** * A CleanupPass implementation that will remove stored scopes from the * MemoizedScopeCreator of the compiler instance for a the hot swapped script. * <p> * This pass will also clear out Source Nodes of Function Types declared on * Vars tracked by MemoizedScopeCreator */ static class MemoizedScopeCleanupPass implements HotSwapCompilerPass { private final AbstractCompiler compiler; public MemoizedScopeCleanupPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ScopeCreator creator = compiler.getTypedScopeCreator(); if (creator instanceof MemoizedScopeCreator) { MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator; String newSrc = scriptRoot.getSourceFileName(); for (Var var : scopeCreator.getAllSymbols()) { JSType type = var.getType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null && newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) { fnType.setSource(null); } } } scopeCreator.removeScopesForScript(originalRoot.getSourceFileName()); } } @Override public void process(Node externs, Node root) { // MemoizedScopeCleanupPass should not do work during process. } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>getSingletonGetterClassName(callNode); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { nextConvention.applySingletonGetter( functionType, getterType, objectType); } @Override public boolean isInlinableFunction(Node n) { return nextConvention.isInlinableFunction(n); } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return nextConvention.getDelegateRelationship(callNode); } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { nextConvention.applyDelegateRelationship( delegateSuperclass, delegateBase, delegator, delegateProxy, findDelegate); } @Override public String getDelegateSuperclassName() { return nextConvention.getDelegateSuperclassName(); } @Override public void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions) { nextConvention.checkForCallingConventionDefiningCalls( n, delegateCallingConventions); } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope<JSType> scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { nextConvention.defineDelegateProxyPrototypeProperties( registry, scope, delegateProxyPrototypes, delegateCallingConventions); } @Override public String getGlobalObject() { return nextConvention.getGlobalObject(); } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return nextConvention.getAssertionFunctions(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { return nextConvention.describeFunctionBind(n, useTypeInfo); } @Override public boolean isPropertyTestFunction(Node call) { return nextConvention.isPropertyTestFunction(call); } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return next

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { // do nothing } @Override public String getAbstractMethodName() { return null; } @Override public String getSingletonGetterClassName(Node callNode) { return null; } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { // do nothing. } @Override public boolean isInlinableFunction(Node n) { Preconditions.checkState(n.isFunction()); return true; } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return null; } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { // do nothing. } @Override public String getDelegateSuperclassName() { return null; } @Override public void checkForCallingConventionDefiningCalls(Node n, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope<JSType> scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public String getGlobalObject() { return "window"; } @Override public boolean isPropertyTestFunction(Node call) { return false; } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return null; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return Collections.emptySet(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.equals("Function.prototype.bind.call")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } } if (callTarget.isGetProp() && callTarget.getLastChild().getString().equals("bind")) { Node maybeFn = callTarget.getFirstChild(); JSType maybeFnType = maybeFn.getJSType(); FunctionType fnType = null; if (useTypeInfo && maybeFnType != null) { fnType = maybeFnType.restrictByNotNullOrUndefined() .toMaybeFunctionType(); } if (fnType != null || maybeFn.isFunction()) { // (function(){}).bind(self, args...); Node thisValue = callTarget.getNext(); Node parameters = safeNext(thisValue); return new Bind(maybeFn, thisValue, parameters); } } return null; } @Override public Collection<String> getIndirectlyDeclaredProperties() { return ImmutableList.of(); } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>isSimpleOp) { msg = "The result of the '" + Token.name(n.getType()).toLowerCase() + "' operator is not being used."; } t.getCompiler().report( t.makeError(n, level, USELESS_CODE_ERROR, msg)); // TODO(johnlenz): determine if it is necessary to // try to protect side-effect free statements as well. if (!NodeUtil.isStatement(n)) { problemNodes.add(n); } } } /** * Protect side-effect free nodes by making them parameters * to a extern function call. This call will be removed * after all the optimizations passes have run. */ private void protectSideEffects() { if (!problemNodes.isEmpty()) { addExtern(); for (Node n : problemNodes) { Node name = IR.name(PROTECTOR_FN).srcref(n); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node replacement = IR.call(name).srcref(n); replacement.putBooleanProp(Node.FREE_CALL, true); n.getParent().replaceChild(n, replacement); replacement.addChildToBack(n); } compiler.reportCodeChange(); } } private void addExtern() { Node name = IR.name(PROTECTOR_FN); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node var = IR.var(name); // Add "@noalias" so we can strip the method when AliasExternals is enabled. JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordNoAlias(); var.setJSDocInfo(builder.build(var)); CompilerInput input = compiler.getSynthesizedExternsInput(); input.getAstRoot(compiler).addChildrenToBack(var); compiler.reportCodeChange(); } /** * Remove side-effect sync functions. */ static class StripProtection extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; StripProtection(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { Node target = n.getFirstChild(); // TODO(johnlenz): add this to the coding convention // so we can remove goog.reflect.sinkValue as well. if (target.isName() && target.getString().equals(PROTECTOR_FN)) { Node expr = n.getLastChild(); n.detachChildren(); parent.replaceChild(n, expr); } } } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> the $ must be * upper case. * $A Constant - doesn't have to be anything in front of the $ * </pre> */ @Override public boolean isConstant(String name) { if (name.length() <= 1) { return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.toUpperCase().equals(name); } /** * {@inheritDoc} * * <p>This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * * <p>Examples: * <ul> * <li>A</li> * <li>213</li> * <li>FOO_BAR</li> * </ul> */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * * <p>In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * * <p>In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> LanguageMode languageMode, boolean acceptConstKeyword) { return createConfig(isIdeMode, languageMode, acceptConstKeyword, null); } public static Config createConfig(boolean isIdeMode, LanguageMode languageMode, boolean acceptConstKeyword, Set<String> extraAnnotationNames) { initResourceConfig(); Set<String> effectiveAnnotationNames; if (extraAnnotationNames == null) { effectiveAnnotationNames = annotationNames; } else { effectiveAnnotationNames = new HashSet<String>(annotationNames); effectiveAnnotationNames.addAll(extraAnnotationNames); } return new Config(effectiveAnnotationNames, suppressionNames, isIdeMode, languageMode, acceptConstKeyword); } public static Set<String> getReservedVars() { initResourceConfig(); return reservedVars; } private static synchronized void initResourceConfig() { if (annotationNames != null) { return; } ResourceBundle config = ResourceBundle.getBundle(configResource); annotationNames = extractList(config.getString("jsdoc.annotations")); suppressionNames = extractList(config.getString("jsdoc.suppressions")); reservedVars = extractList(config.getString("compiler.reserved.vars")); } private static Set<String> extractList(String configProp) { String[] names = configProp.split(","); Set<String> trimmedNames = Sets.newHashSet(); for (String name : names) { trimmedNames.add(name.trim()); } return ImmutableSet.copyOf(trimmedNames); } /** * Parses the JavaScript text given by a reader. * * @param sourceString Source code from the file. * @param errorReporter An error. * @param logger A logger. * @return The AST of the given text. * @throws IOException */ public static ParseResult parse(StaticSourceFile sourceFile, String sourceString, Config config, ErrorReporter errorReporter, Logger logger) throws IOException { Context cx = Context.enter(); cx.setErrorReporter(errorReporter); cx.setLanguageVersion(Context.VERSION_1_5); CompilerEnvirons compilerEnv = new CompilerEnvirons(); compilerEnv.initFromContext(cx); compilerEnv.setRecordingComments(true); compilerEnv.set

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>State(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.isCall()); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); if (!NodeUtil.isGet(first)) { n.putBooleanProp(Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.isName() && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node. */ private void annotateDispatchers(Node n, Node parent) { Preconditions.checkState(n.isFunction()); if (parent.getJSDocInfo() != null && parent.getJSDocInfo().isJavaDispatch()) { if (parent.isAssign()) { Preconditions.checkState(parent.getLastChild() == n); n.putBooleanProp(Node.IS_DISPATCHER, true); } } } /** * In the AST that Rhino gives us, it needs to make a distinction * between JsDoc on the object literal node and JsDoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ private void normalizeObjectLiteralKeyAnnotations(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> t.makeError(n, MISSING_EXTENDS_TAG_WARNING, subObject.toString()))); } else { mismatch(t.getSourceName(), n, "mismatch in declaration of superclass type", superObject, declaredSuper); } // Correct the super type. if (!subCtor.hasCachedValues()) { subCtor.setPrototypeBasedOn(superObject); } } } /** * Expect that the first type can be cast to the second type. The first type * must have some relationship with the second. * * @param t The node traversal. * @param n The node where warnings should point. * @param type The type being cast from. * @param castType The type being cast to. */ void expectCanCast(NodeTraversal t, Node n, JSType castType, JSType type) { if (!type.canCastTo(castType)) { registerMismatch(type, castType, report(t.makeError(n, INVALID_CAST, type.toString(), castType.toString()))); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. * @return The variable we end up with. Most of the time, this will just * be {@code var}, but in some rare cases we will need to declare * a new var with new source info. */ Var expectUndeclaredVariable(String sourceName, CompilerInput input, Node n, Node parent, Var var, String variableName, JSType newType) { Var newVar = var; boolean allowDupe = false; if (n.isGetProp() || NodeUtil.isObjectLitKey(n, parent)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.isEquivalentTo(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.isGetProp()) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); if (objectType.getConstructor() != null && objectType.getConstructor().isInterface()) { objectType = FunctionType.getTopDefiningInterface( objectType, propName); } else { // classes while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type.isFunctionType()) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType();

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> { // Check for the typeof operator. int operatorToken = condition.getType(); switch (operatorToken) { case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.isTypeOf() && right.isString()) { typeOfNode = left; stringNode = right; } else if (right.isTypeOf() && left.isString()) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } case Token.NE: if (outcome) { return caseEquality(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().isString()) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(),

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> { ungetChar(c2); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { do { c1 = c2; c2 = getChar(); if (c1 == '.' && c2 == '<') { ungetChar(c2); ungetChar(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { if (isJSDocString(c2)) { addToString(c1); } else { ungetChar(c2); addToString(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } } } while (true); } } } } } /** * Gets the remaining JSDoc line without the {@link JsDocToken#EOL}, * {@link JsDocToken#EOF} or {@link JsDocToken#EOC}. */ @SuppressWarnings("fallthrough") String getRemainingJSDocLine() { int c; for (;;) { c = getChar(); switch (c) { case '*': if (peekChar() != '/') { addToString(c); break; } // fall through case EOF_CHAR: case '\n': ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return this.string; default: addToString(c); break; } } } final int getLineno() { return lineno; } final int getCharno() { return lineno == initLineno? initCharno + charno : charno; } final String getString() { return string; } final boolean eof() { return hitEOF; } private String getStringFromBuffer() { tokenEnd = cursor; return new String(stringBuffer, 0, stringBufferTop); } private void addToString(int c) { int N = stringBufferTop; if (N == stringBuffer.length) { char[] tmp = new char[stringBuffer.length * 2]; System.arraycopy(stringBuffer

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>_THIS = DiagnosticType.warning( "JSC_USED_GLOBAL_THIS", "dangerous use of the global 'this' object"); private final AbstractCompiler compiler; /** * If {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler) { this.compiler = compiler; } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING_KEY)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent, gramps)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype")) { return false; }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } } if (parent != null && parent.isAssign()) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (NodeUtil.isGet(lhs)) { if (lhs.isGetProp() && lhs.getLastChild().getString().equals("prototype")) { return false; } Node llhs = lhs.getFirstChild(); if (llhs.isGetProp() && llhs.getLastChild().getString().equals("prototype")) { return false; } } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isThis() && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. return parent != null && NodeUtil.isGet(parent); } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} * ... x = function() {}; * var ... x = function() {}; * ... var x = function() {}; * </pre> */ private JSDocInfo getFunctionJsDocInfo(Node n) { JSDocInfo jsDoc = n.getJSDocInfo(); Node parent = n.getParent(); if (jsDoc == null) { int

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> parentType = parent.getType(); if (parentType == Token.NAME || parentType == Token.ASSIGN) { jsDoc = parent.getJSDocInfo(); if (jsDoc == null && parentType == Token.NAME) { Node gramps = parent.getParent(); if (gramps.isVar()) { jsDoc = gramps.getJSDocInfo(); } } } } return jsDoc; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; /** * Information about the context in which a Definition is used. * Includes the referring node, and context in which the reference * occurs - including the module in which the reference appears. * */ class UseSite { final Node node; final Scope scope; final JSModule module; UseSite(Node node, Scope scope, JSModule module) { this.node = node; this.scope = scope; this.module = module; } // Use the node as the identifying feature to make the UseSite recreatable. @Override public int hashCode() { return this.node.hashCode(); } @Override public boolean equals(Object o) { return (o instanceof UseSite && ((UseSite)(o)).node.equals(this.node)); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * NodeTraversal allows an iteration through the nodes in the parse tree, * and facilitates the optimizations on the parse tree. * */ public class NodeTraversal { // Package protected for tests private final AbstractCompiler compiler; private final Callback callback; /** Contains the current node*/ private Node curNode; public static final DiagnosticType NODE_TRAVERSAL_ERROR = DiagnosticType.error("JSC_NODE_TRAVERSAL_ERROR", "{0}"); /** * Stack containing the Scopes that have been created. The Scope objects * are lazily created; so the {@code scopeRoots} stack contains the * Nodes for all Scopes that have not been created yet. */ private final Deque<Scope> scopes = new ArrayDeque<Scope>(); /** * A stack of scope roots. All scopes that have not been created * are represented in this Deque. */ private final Deque<Node> scopeRoots = new ArrayDeque<Node>(); /** * Stack of control flow graphs (CFG). There is one CFG per scope. CFGs

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); inputId = NodeUtil.getInputId(scopeRoot); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { String sourceFileName = getBestSourceFileName(n); if (sourceFileName == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceFileName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceFileName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); inputId = null; sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.isFunction()) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. if (inputId == null) { inputId = NodeUtil.getInputId(n);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@code null} */ protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { Preconditions.checkNotNull(parent); if (refinedScope != null && getScope() != refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Expression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.isBlock(), body); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Iterator<Node> it = scopeRoots.descendingIterator(); while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph<Node> getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return cfgs.peek(); } /** Returns the current scope's root. */ public Node getScopeRoot() { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } /** * Determines whether the traversal is currently in the global scope. */ boolean inGlobalScope() { return getScopeDepth() <= 1; } int getScopeDepth() { return scopes.size() + scopeRoots.size(); } public boolean hasScope() { return !(scopes.isEmpty() && scopeRoots.isEmpty()); } /** Reports a diagnostic (error or warning) */ public void report(Node n, DiagnosticType diagnosticType, String... arguments) { JSError error = JSError.make(getBestSourceFileName(n), n, diagnosticType, arguments); compiler.report(error); } private static String getSourceName(Node n) { String name = n.getSourceFileName(); return name == null ? "" : name; } InputId getInputId() { return inputId; } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, CheckLevel level, DiagnosticType type, String... arguments) { return JSError.make(getBestSourceFileName(n), n, level, type, arguments); } /** * Creates a JSError during Node

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>)); block.addChildToBack(stmt); } return block; } public static Node script(List<Node> stmts) { Node paramList = script(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatementNoReturn(stmt)); paramList.addChildToBack(stmt); } return paramList; } public static Node var(Node name, Node value) { Preconditions.checkState(name.isName() && !name.hasChildren()); Preconditions.checkState(mayBeExpression(value)); name.addChildToFront(value); return var(name); } public static Node var(Node name) { Preconditions.checkState(name.isName()); return new Node(Token.VAR, name); } public static Node returnNode() { return new Node(Token.RETURN); } public static Node returnNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.RETURN, expr); } public static Node throwNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.THROW, expr); } public static Node exprResult(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.EXPR_RESULT, expr); } public static Node ifNode(Node cond, Node then) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); return new Node(Token.IF, cond, then); } public static Node ifNode(Node cond, Node then, Node elseNode) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); Preconditions.checkState(elseNode.isBlock()); return new Node(Token.IF, cond, then, elseNode); } public static Node doNode(Node body, Node cond) { Preconditions.checkState(body.isBlock()); Preconditions.checkState(mayBeExpression(cond)); return new Node(Token.DO, body, cond); } public static Node forIn(Node target, Node cond, Node body) { Preconditions.checkState(target.isVar() ||

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Node catchBody = block().copyInformationFrom(tryBody); return new Node(Token.TRY, tryBody, catchBody, finallyBody); } public static Node tryCatch(Node tryBody, Node catchNode) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(catchNode.isCatch()); Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode); return new Node(Token.TRY, tryBody, catchBody); } public static Node tryCatchFinally( Node tryBody, Node catchNode, Node finallyBody) { Preconditions.checkState(finallyBody.isBlock()); Node tryNode = tryCatch(tryBody, catchNode); tryNode.addChildToBack(finallyBody); return tryNode; } public static Node catchNode(Node expr, Node body) { Preconditions.checkState(expr.isName()); Preconditions.checkState(body.isBlock()); return new Node(Token.CATCH, expr, body); } public static Node breakNode() { return new Node(Token.BREAK); } public static Node breakNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.BREAK, name); } public static Node continueNode() { return new Node(Token.CONTINUE); } public static Node continueNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.CONTINUE, name); } // public static Node call(Node target, Node ... args) { Node call = new Node(Token.CALL, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); call.addChildToBack(arg); } return call; } public static Node newNode(Node target, Node ... args) { Node newcall = new Node(Token.NEW, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); newcall.addChildToBack(arg); } return newcall; } public static Node name(String name) { return Node.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>newString(Token.NAME, name); } public static Node getprop(Node target, Node prop) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(prop.isString()); return new Node(Token.GETPROP, target, prop); } public static Node getelem(Node target, Node elem) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(mayBeExpression(elem)); return new Node(Token.GETELEM, target, elem); } public static Node assign(Node target, Node expr) { Preconditions.checkState(isAssignmentTarget(target)); Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.ASSIGN, target, expr); } public static Node hook(Node cond, Node trueval, Node falseval) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(mayBeExpression(trueval)); Preconditions.checkState(mayBeExpression(falseval)); return new Node(Token.HOOK, cond, trueval, falseval); } public static Node comma(Node expr1, Node expr2) { return binaryOp(Token.COMMA, expr1, expr2); } public static Node and(Node expr1, Node expr2) { return binaryOp(Token.AND, expr1, expr2); } public static Node or(Node expr1, Node expr2) { return binaryOp(Token.OR, expr1, expr2); } public static Node not(Node expr1) { return unaryOp(Token.NOT, expr1); } /** * "==" */ public static Node eq(Node expr1, Node expr2) { return binaryOp(Token.EQ, expr1, expr2); } /** * "===" */ public static Node sheq(Node expr1, Node expr2) { return binaryOp(Token.SHEQ, expr1, expr2); } public static Node voidNode(Node expr1) { return unaryOp(Token.VOID, expr1); } public static Node neg(Node expr1) { return unaryOp(Token.NEG, expr1); } public static Node pos(Node expr1)

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>BeStatementNoReturn(n)) { return n.isReturn(); } return true; } /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeExpression(Node n) { switch (n.getType()) { case Token.FUNCTION: // FUNCTION is used both in expression and statement // contexts. return true; case Token.ADD: case Token.AND: case Token.ARRAYLIT: case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.BITAND: case Token.BITOR: case Token.BITNOT: case Token.BITXOR: case Token.CALL: case Token.COMMA: case Token.DEC: case Token.DELPROP: case Token.DIV: case Token.EQ: case Token.FALSE: case Token.GE: case Token.GETPROP: case Token.GETELEM: case Token.GT: case Token.HOOK: case Token.IN: case Token.INC: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NAME: case Token.NE: case Token.NEG: case Token.NEW: case Token.NOT: case Token.NUMBER: case Token.NULL: case Token.OBJECTLIT: case Token.OR: case Token.POS: case Token.REGEXP: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.STRING: case Token.SUB: case Token.THIS: case Token.TYPEOF: case Token.TRUE: case Token.URSH: case Token.VOID: return true; default:

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> private final Set<String> exposedProperties = Sets.newHashSet(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isExprAssign(n)) { Node assign = n.getFirstChild(); Node lhs = assign.getFirstChild(); if (lhs.isGetProp() && isMarkedExpose(assign)) { exposedProperties.add(lhs.getLastChild().getString()); } } else if (n.isStringKey() && isMarkedExpose(n)) { exposedProperties.add(n.getString()); } } private boolean isMarkedExpose(Node n) { JSDocInfo info = n.getJSDocInfo(); return info != null && info.isExpose(); } } /** * Rewrite all exposed properties in [] form. */ private class RewriteExposedProperties extends AbstractPostOrderCallback { private final Set<String> exposedProperties; RewriteExposedProperties(Set<String> exposedProperties) { this.exposedProperties = exposedProperties; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { String propName = n.getLastChild().getString(); if (exposedProperties.contains(propName)) { Node obj = n.removeFirstChild(); Node prop = n.removeFirstChild(); n.getParent().replaceChild(n, IR.getelem(obj, prop)); compiler.reportCodeChange(); } } else if (n.isStringKey()) { String propName = n.getString(); if (exposedProperties.contains(propName)) { n.setQuotedString(); compiler.reportCodeChange(); } } } } /** * Propagate constant annotations over the Var graph. */ static class PropagateConstantAnnotationsOverVars extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; PropagateConstantAnnotationsOverVars( AbstractCompiler compiler, boolean forbidChanges) { this.compiler = compiler; this.assertOnChange = forbidChanges; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverseRoots(externs,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.isName()) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top-level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } boolean shouldBeConstant = (info != null && info.isConstant()) || NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent); boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (shouldBeConstant && !isMarkedConstant) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String, Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; CodingConvention convention = compiler.getCodingConvention(); if (NodeUtil.isConstantName(n) || NodeUtil.isConstantByConvention(convention, n, parent)) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name %s is not annotated as constant.", name); } else { Preconditions.checkState(expectedConst == isConst, "The name %s should not be annotated as constant.", name); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name %s is not consistently annotated as constant.", name); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block. * - Add constant annotations based on coding convention. */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } @Override public boolean shouldTraverse(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>NodeTraversal t, Node n, Node parent) { doStatementNormalizations(t, n, parent); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); Node empty = IR.empty(); empty.copyInformationFrom(n); n.addChildBefore(empty, expr); n.addChildAfter(empty.cloneNode(), expr); reportCodeChange("WHILE node"); } break; case Token.FUNCTION: normalizeFunctionDeclaration(n); break; case Token.NAME: case Token.STRING: case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) { annotateConstantsByConvention(n, parent); } break; case Token.CAST: parent.replaceChild(n, n.removeFirstChild()); break; } } /** * Mark names and properties that are constants by convention. */ private void annotateConstantsByConvention(Node n, Node parent) { Preconditions.checkState( n.isName() || n.isString() || n.isStringKey() || n.isGetterDef() || n.isSetterDef()); // There are only two cases where a string token // may be a variable reference: The right side of a GETPROP // or an OBJECTLIT key. boolean isObjLitKey = NodeUtil.isObjectLitKey(n, parent); boolean isProperty = isObjLitKey || (parent.isGetProp() && parent.getLastChild() == n); if (n.isName() || isProperty) { boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (!isMarkedConstant && NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent)) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name +

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ private void normalizeFunctionDeclaration(Node n) { Preconditions.checkState(n.isFunction()); if (!NodeUtil.isFunctionExpression(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { rewriteFunctionDeclaration(n); } } /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ private void rewriteFunctionDeclaration(Node n) { // Prepare a spot for the function. Node oldNameNode = n.getFirstChild(); Node fnNameNode = oldNameNode.cloneNode(); Node var = IR.var(fnNameNode).srcref(n); // Prepare the function oldNameNode.setString(""); // Move the function Node parent = n.getParent(); parent.replaceChild(n, var); fnNameNode.addChildToFront(n); reportCodeChange("Function declaration"); } /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations( NodeTraversal t, Node n, Node parent) { if (n.isLabel()) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.isLabel()) { extractForInitializer(n, null, null); } // Only inspect the children of SCRIPTs, BLOCKs, as all these // are the only legal place for VARs. if (NodeUtil.isStatementBlock

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> {} // to: // var a = 1; for (a in b) {}; Node newStatement = first; // Clone just the node, to remove any initialization. Node name = newStatement.getFirstChild().cloneNode(); first.getParent().replaceChild(first, name); insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR-IN var declaration"); } } else if (!c.getFirstChild().isEmpty()) { Node init = c.getFirstChild(); Node empty = IR.empty(); empty.copyInformationFrom(c); c.replaceChild(init, empty); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.isVar()) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect. */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.isVar()) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = IR.var(name).srcref(n); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) { Preconditions.checkState( functionBody.getParent().isFunction()); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> declarations at the beginning of the function body, they // are already in the right place. while (current != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Read the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node externs, Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverseRoots(externs, root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler implements SyntacticScopeCreator.RedeclarationHandler { private Set<Var> hasOkDuplicateDeclaration = Sets.newHashSet(); /** * Remove duplicate VAR declarations encountered discovered during * scope creation. */ @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Preconditions.checkState(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>n.isName()); Node parent = n.getParent(); Var v = s.getVar(name); if (v != null && s.isGlobal()) { // We allow variables to be duplicate declared if one // declaration appears in source and the other in externs. // This deals with issues where a browser built-in is declared // in one browser but not in another. if (v.isExtern() && !input.isExtern()) { if (hasOkDuplicateDeclaration.add(v)) { return; } } } // If name is "arguments", Var maybe null. if (v != null && v.getParentNode().isCatch()) { // Redeclaration of a catch expression variable is hard to model // without support for "with" expressions. // The ECMAScript spec (section 12.14), declares that a catch // "catch (e) {}" is handled like "with ({'e': e}) {}" so that // "var e" would refer to the scope variable, but any following // reference would still refer to "e" of the catch expression. // Until we have support for this disallow it. // Currently the Scope object adds the catch expression to the // function scope, which is technically not true but a good // approximation for most uses. // TODO(johnlenz): Consider improving how scope handles catch // expression. // Use the name of the var before it was made unique. name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName( name); compiler.report( JSError.make( input.getName(), n, CATCH_BLOCK_VAR_ERROR, name)); } else if (v != null && parent.isFunction()) { if (v.getParentNode().isVar()) { s.undeclare(v); s.declare(name, n, n.getJSType(), v.input); replaceVarWithAssignment(v.getNameNode(), v.getParentNode(), v.getParentNode().getParent()); } } else if (parent.isVar()) { Preconditions.checkState(parent.hasOneChild()); replaceVarWithAssignment(n, parent, parent.getParent()); } } /** * Remove the parent VAR. There

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> */ public interface StaticScope<T> { /** * Returns the root node associated with this scope. May be null. */ Node getRootNode(); /** Returns the scope enclosing this one or null if none. */ StaticScope<T> getParentScope(); /** * Returns any defined slot within this scope for this name. This call * continues searching through parent scopes if a slot with this name is not * found in the current scope. * @param name The name of the variable slot to look up. * @return The defined slot for the variable, or {@code null} if no * definition exists. */ StaticSlot<T> getSlot(String name); /** Like {@code getSlot} but does not recurse into parent scopes. */ StaticSlot<T> getOwnSlot(String name); /** Returns the expected type of {@code this} in the current scope. */ T getTypeOfThis(); }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> an externs input) by input id. * May return null. */ public abstract CompilerInput getInput(InputId inputId); /** * Looks up a source file by name. May return null. */ abstract SourceFile getSourceFileByName(String sourceName); /** * Creates a new externs file. * @param name A name for the new externs file. * @throws IllegalArgumentException If the name of the externs file conflicts * with a pre-existing externs file. */ abstract CompilerInput newExternInput(String name); /** * Gets the module graph. May return null if there aren't at least two * modules. */ abstract JSModuleGraph getModuleGraph(); /** * Gets the inputs in the order in which they are being processed. * Only for use by {@code AbstractCompilerRunner}. */ abstract List<CompilerInput> getInputsInOrder(); /** * Gets a central registry of type information from the compiled JS. */ public abstract JSTypeRegistry getTypeRegistry(); /** * Gets a memoized scope creator with type information. */ abstract ScopeCreator getTypedScopeCreator(); /** * Gets the top scope. */ public abstract Scope getTopScope(); /** * Report an error or warning. */ public abstract void report(JSError error); /** * Report an internal error. */ abstract void throwInternalError(String msg, Exception cause); /** * Gets the current coding convention. */ public abstract CodingConvention getCodingConvention(); /** * Report code changes. */ public abstract void reportCodeChange(); /** * Logs a message under a central logger. */ abstract void addToDebugLog(String message); /** * Sets the CssRenamingMap. */ abstract void setCssRenamingMap(CssRenamingMap map); /** * Gets the CssRenamingMap. */ abstract CssRenamingMap getCssRenamingMap(); /** * Gets a suitable SCRIPT node to serve as a parent for code insertion. If * {@code module} contains any inputs, the returned node will be the SCRIPT * node corresponding to its first input. If {@code module} is empty, on the * other hand, then the returned node will be the first SCRIPT node in a

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; import java.util.Set; /** * Computes "may be" reaching use for all definitions of each variables. * * A use of {@code A} in {@code alert(A)} is a "may be" reaching use of * the definition of {@code A} at {@code A = foo()} if at least one path from * the use node reaches that definition and it is the last definition before * the use on that path. * */ class MaybeReachingVariableUse extends DataFlowAnalysis<Node, MaybeReachingVariableUse.ReachingUses> { // The scope of the function that we are analyzing. private final Scope jsScope; private final Set<Var> escaped; MaybeReachingVariableUse( ControlFlowGraph<Node> cfg

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>, Scope jsScope, AbstractCompiler compiler) { super(cfg, new ReachingUsesJoinOp()); this.jsScope = jsScope; this.escaped = Sets.newHashSet(); // TODO(user): Maybe compute it somewhere else and re-use the escape // local set here. computeEscaped(jsScope, escaped, compiler); } /** * May use definition lattice representation. It captures a product * lattice for each local (non-escaped) variable. The sub-lattice is * a n + 2 power set element lattice with all the Nodes in the program, * TOP and BOTTOM. This is better explained with an example: * * Consider: A sub-lattice element representing the variable A represented * by { N_4, N_5} where N_x is a Node in the program. This implies at * that particular point in the program the content of A is "upward exposed" * at point N_4 and N_5. * * Example: * * A = 1; * ... * N_3: * N_4: print(A); * N_5: y = A; * N_6: A = 1; * N_7: print(A); * * At N_3, reads of A in {N_4, N_5} are said to be upward exposed. */ static final class ReachingUses implements LatticeElement { final Multimap<Var, Node> mayUseMap; public ReachingUses() { mayUseMap = HashMultimap.create(); } /** * Copy constructor. * * @param other The constructed object is a replicated copy of this element. */ public ReachingUses(ReachingUses other) { mayUseMap = HashMultimap.create(other.mayUseMap); } @Override public boolean equals(Object other) { return (other instanceof ReachingUses) && ((ReachingUses) other).mayUseMap.equals(this.mayUseMap); } @Override public int hashCode() { return mayUseMap.hashCode(); } } /** * The join is a simple union because of the "may be" nature of the analysis. * * Consider:

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> A = 1; if (x) { A = 2 }; alert(A); * * The read of A "may be" exposed to A = 1 in the beginning. */ private static class ReachingUsesJoinOp implements JoinOp<ReachingUses> { @Override public ReachingUses apply(List<ReachingUses> from) { ReachingUses result = new ReachingUses(); for (ReachingUses uses : from) { result.mayUseMap.putAll(uses.mayUseMap); } return result; } } @Override boolean isForward() { return false; } @Override ReachingUses createEntryLattice() { return new ReachingUses(); } @Override ReachingUses createInitialEstimateLattice() { return new ReachingUses(); } @Override ReachingUses flowThrough(Node n, ReachingUses input) { ReachingUses output = new ReachingUses(input); // If there's an ON_EX edge, this cfgNode may or may not get executed. // We can express this concisely by just pretending this happens in // a conditional. boolean conditional = hasExceptionHandler(n); computeMayUse(n, n, output, conditional); return output; } private boolean hasExceptionHandler(Node cfgNode) { List<DiGraphEdge<Node, Branch>> branchEdges = getCfg().getOutEdges(cfgNode); for (DiGraphEdge<Node, Branch> edge : branchEdges) { if (edge.getValue() == Branch.ON_EX) { return true; } } return false; } private void computeMayUse( Node n, Node cfgNode, ReachingUses output, boolean conditional) { switch (n.getType()) { case Token.BLOCK: case Token.FUNCTION: return; case Token.NAME: addToUseIfLocal(n.getString(), cfgNode, output); return; case Token.WHILE: case Token.DO: case Token.IF: computeMayUse( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeMayUse(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> NodeUtil.getConditionExpression(n), cfgNode, output, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (lhs.isVar()) { lhs = lhs.getLastChild(); // for(var x in y) {...} } if (lhs.isName() && !conditional) { removeFromUseIfLocal(lhs.getString(), output); } computeMayUse(rhs, cfgNode, output, conditional); } return; case Token.AND: case Token.OR: computeMayUse(n.getLastChild(), cfgNode, output, true); computeMayUse(n.getFirstChild(), cfgNode, output, conditional); return; case Token.HOOK: computeMayUse(n.getLastChild(), cfgNode, output, true); computeMayUse(n.getFirstChild().getNext(), cfgNode, output, true); computeMayUse(n.getFirstChild(), cfgNode, output, conditional); return; case Token.VAR: Node varName = n.getFirstChild(); Preconditions.checkState(n.hasChildren(), "AST should be normalized"); if (varName.hasChildren()) { computeMayUse(varName.getFirstChild(), cfgNode, output, conditional); if (!conditional) { removeFromUseIfLocal(varName.getString(), output); } } return; default: if (NodeUtil.isAssignmentOp(n) && n.getFirstChild().isName()) { Node name = n.getFirstChild(); if (!conditional) { removeFromUseIfLocal(name.getString(), output); } // In case of a += "Hello". There is a read of a. if (!n.isAssign()) { addToUseIfLocal(name.getString(), cfgNode, output); } computeMayUse(name.getNext(), cfgNode, output, conditional); } else { /* * We want to traverse in reverse order because we want the LAST * definition in the sub-tree.... * But we have no better way to traverse in reverse other :'( */ for (Node c = n.getLastChild(); c != null; c = n.getChildBefore(c)) { computeMayUse

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(c, cfgNode, output, conditional); } } } } /** * Sets the variable for the given name to the node value in the upward * exposed lattice. Do nothing if the variable name is one of the escaped * variable. */ private void addToUseIfLocal(String name, Node node, ReachingUses use) { Var var = jsScope.getVar(name); if (var == null || var.scope != jsScope) { return; } if (!escaped.contains(var)) { use.mayUseMap.put(var, node); } } /** * Removes the variable for the given name from the node value in the upward * exposed lattice. Do nothing if the variable name is one of the escaped * variable. */ private void removeFromUseIfLocal(String name, ReachingUses use) { Var var = jsScope.getVar(name); if (var == null || var.scope != jsScope) { return; } if (!escaped.contains(var)) { use.mayUseMap.removeAll(var); } } /** * Gets a list of nodes that may be using the value assigned to {@code name} * in {@code defNode}. {@code defNode} must be one of the control flow graph * nodes. * * @param name name of the variable. It can only be names of local variable * that are not function parameters, escaped variables or variables * declared in catch. * @param defNode The list of upward exposed use for the variable. */ Collection<Node> getUses(String name, Node defNode) { GraphNode<Node, Branch> n = getCfg().getNode(defNode); Preconditions.checkNotNull(n); FlowState<ReachingUses> state = n.getAnnotation(); return state.getOut().mayUseMap.get(jsScope.getVar(name)); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>TypeNative. private final boolean isChecked; UnknownType(JSTypeRegistry registry, boolean isChecked) { super(registry); this.isChecked = isChecked; } @Override public boolean isUnknownType() { return true; } @Override public boolean isCheckedUnknownType() { return isChecked; } @Override public boolean canBeCalled() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseUnknownType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseUnknownType(this, that); } @Override String toStringHelper(boolean forAnnotations) { return getReferenceName(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing to define return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public FunctionType getConstructor() { return null; } @Override public String getReferenceName() { return isChecked ? "??" : "?"; } @Override public String getDisplayName() { return "Unknown"; } @Override public boolean hasDisplayName() { return true; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>); // Note we use the global scope to prevent wrong "undefined-var errors" on // variables that are defined in other JS files. t.traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); // TODO(bashir) Check if we need to createSynthesizedExternVar like process. } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(parent.isFunction()); Preconditions.checkState(NodeUtil.isFunctionExpression(parent)); return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { // The extern checks are stricter, don't report a second error. if (!strictExternCheck || !t.getInput().isExtern()) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (!sanityCheck && varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * Create a new variable in a synthetic script. This will prevent * subsequent compiler passes from crashing. */ private void createSynthesizedExternVar(String varName) { Node nameNode = IR.name(varName); // Mark the variable as constant if it matches the coding convention // for constant vars. // NOTE(nicksantos): honestly, I'm not sure how much this matters. // AFAIK, all people who use the CONST coding convention also // compile with undeclaredVars as errors. We have some test // cases for this configuration though, and it makes them happier. if (compiler.getCodingConvention().isConstant(varName)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } getSynthesizedExternsRoot().addChildToBack( IR.var(nameNode)); varsToDeclareInExterns.remove(varName); compiler.reportCodeChange(); } /** * A check for name references in the externs inputs. These

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.PARAM_LIST: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); varsToDeclareInExterns.add(n.getString()); } } break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { varsToDeclareInExterns.add(n.getString()); } break; } } } } /** Lazily create a "new" externs input for undeclared variables. */ private CompilerInput getSynthesizedExternsInput() { return compiler.getSynthesizedExternsInput(); } /** Lazily create a "new" externs root for undeclared variables. */ private Node getSynthesizedExternsRoot() { if (synthesizedExternsRoot == null) { CompilerInput synthesizedExterns = getSynthesizedExternsInput(); synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler); } return synthesizedExternsRoot; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.keySet(); } @Override public Scope getScope(Var var) { return var.scope; } /** * Gets the reference collection for the given variable. */ @Override public ReferenceCollection getReferences(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(t, v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block stack and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explicit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(NodeTraversal t, Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference, t, v); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map<Var, ReferenceCollection> referenceMap; public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); } } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); } static Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} }; /** * A collection of references.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection implements Iterable<Reference> { List<Reference> references = Lists.newArrayList(); @Override public Iterator<Reference> iterator() { return references.iterator(); } void add(Reference reference, NodeTraversal t, Var v) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means that we don't * have enough information to make a definitive judgment. */ protected boolean isWellDefined() { int size = references.size(); if (size == 0) { return false; } // If this is a declaration that does not instantiate the variable, // it's not well-defined. Reference init = getInitializingReference(); if (init == null) { return false; } Preconditions.checkState(references.get(0).isDeclaration()); BasicBlock initBlock = init.getBasicBlock(); for (int i = 1; i < size; i++) { if (!initBlock.provablyExecutesBefore( references.get(i).getBasicBlock())) { return false; } } return true; } /** * Whether the variable is escaped into an inner scope. */ boolean isEscaped() { Scope scope = null; for (Reference ref : references) { if (scope == null) { scope = ref.scope; } else if (scope != ref.scope) { return true; } } return false; } /** * @param index The index into the references array to look for an * assigning declaration. * * This is either the declaration if a value is assigned (such as * "var a = 2", "function a()...", "... catch (a)..."). */ private boolean isInitializingDeclarationAt(int index) { Reference maybeInit = references.get(index); if (maybeInit.is

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } else if (block.isLoop) { return false; } } return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { if (assignment == null) { assignment = ref; } else { return null; } } } return assignment; } /** * @return Whether the variable is never assigned a value. */ boolean isNeverAssigned() { int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { return false; } } return true; } boolean firstReferenceIsAssigningDeclaration() { int size = references.size(); if (size > 0 && references.get(0).isInitializingDeclaration()) { return true; } return false; } } /** * Represents a single declaration or reference to a variable. */ static final class Reference implements StaticReference<JSType> { private static final Set<Integer> DECLARATION_PARENTS = ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH); private final Node nameNode; private final BasicBlock basicBlock; private final Scope scope; private final InputId inputId; private final StaticSourceFile sourceFile; Reference(Node nameNode, NodeTraversal t, BasicBlock basicBlock) { this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId()); } // Bleeding functions are weird, because the declaration does // not appear inside their scope. So they need their own constructor. static Reference newBleedingFunction(NodeTraversal t, BasicBlock basicBlock, Node func) { return new Reference(func.getFirstChild(), basicBlock, t.getScope(), t.getInput().

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>getInputId()); } /** * Creates a variable reference in a given script file name, used in tests. * * @return The created reference. */ @VisibleForTesting static Reference createRefForTest(CompilerInput input) { return new Reference(new Node(Token.NAME), null, null, input.getInputId()); } private Reference(Node nameNode, BasicBlock basicBlock, Scope scope, InputId inputId) { this.nameNode = nameNode; this.basicBlock = basicBlock; this.scope = scope; this.inputId = inputId; this.sourceFile = nameNode.getStaticSourceFile(); } /** * Makes a copy of the current reference using a new Scope instance. */ Reference cloneWithNewScope(Scope newScope) { return new Reference(nameNode, basicBlock, newScope, inputId); } @Override public Var getSymbol() { return scope.getVar(nameNode.getString()); } @Override public Node getNode() { return nameNode; } public InputId getInputId() { return inputId; } @Override public StaticSourceFile getSourceFile() { return sourceFile; } boolean isDeclaration() { Node parent = getParent(); Node grandparent = parent.getParent(); return DECLARATION_PARENTS.contains(parent.getType()) || parent.isParamList() && grandparent.isFunction(); } boolean isVarDeclaration() { return getParent().isVar(); } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(getParent()); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return isDeclaration() && !getParent().isVar() || nameNode.getFirstChild() != null; } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { Node parent = getParent(); return (parent.isFunction()) ? parent : NodeUtil.getAssignedValue(nameNode); } Basic

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Block getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ private final boolean isFunction; /** * Whether this block denotes a loop. */ private final boolean isLoop; /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); this.isFunction = root.isFunction(); if (root.getParent() != null) { int pType = root.getParent().getType(); this.isLoop = p

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, UNKNOWN_OVERRIDE, INTERFACE_METHOD_OVERRIDE, UNKNOWN_EXPR_TYPE, UNRESOLVED_TYPE, WRONG_ARGUMENT_COUNT, ILLEGAL_IMPLICIT_CAST, INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, EXPECTED_THIS_TYPE, IN_USED_WITH_STRUCT, ILLEGAL_PROPERTY_CREATION, ILLEGAL_OBJLIT_KEY, RhinoErrorReporter.TYPE_PARSE_ERROR, TypedScopeCreator.UNKNOWN_LENDS, TypedScopeCreator.LENDS_ON_NON_OBJECT, TypedScopeCreator.CTOR_INITIALIZER, TypedScopeCreator.IFACE_INITIALIZER, FunctionTypeBuilder.THIS_TYPE_NON_OBJECT); private final AbstractCompiler compiler; private final TypeValidator validator; private final ReverseAbstractInterpreter reverseInterpreter; private final JSTypeRegistry typeRegistry; private Scope topScope; private MemoizedScopeCreator scopeCreator; private final CheckLevel reportMissingOverride; private final CheckLevel reportUnknownTypes; // This may be expensive, so don't emit these warnings if they're // explicitly turned off. private boolean reportMissingProperties = true; private InferJSDocInfo inferJSDocInfo = null; // These fields are used to calculate the percentage of expressions typed. private int typedCount = 0; private int nullCount = 0; private int unknownCount = 0; private boolean inExterns; // A state boolean to see we are currently in @notypecheck section of the // code. private int noTypeCheckSection = 0; public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, Scope topScope, MemoizedScopeCreator scopeCreator, CheckLevel reportMissingOverride, CheckLevel reportUnknownTypes) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.reverseInterpreter = reverseInterpreter; this.typeRegistry = typeRegistry; this.topScope = topScope; this.scopeCreator = scopeCreator; this.reportMissingOverride = reportMissingOverride; this.reportUnknownTypes = reportUnknownTypes; this.inferJSDocInfo

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = new InferJSDocInfo(compiler); } public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, CheckLevel reportMissingOverride, CheckLevel reportUnknownTypes) { this(compiler, reverseInterpreter, typeRegistry, null, null, reportMissingOverride, reportUnknownTypes); } TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry) { this(compiler, reverseInterpreter, typeRegistry, null, null, CheckLevel.WARNING, CheckLevel.OFF); } /** Turn on the missing property check. Returns this for easy chaining. */ TypeCheck reportMissingProperties(boolean report) { reportMissingProperties = report; return this; } /** * Main entry point for this phase of processing. This follows the pattern for * JSCompiler phases. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Preconditions.checkNotNull(scopeCreator); Preconditions.checkNotNull(topScope); Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); if (externsRoot != null) { check(externsRoot, true); } check(jsRoot, false); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope;

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { if (enterSection) { noTypeCheckSection++; } else { noTypeCheckSection--; } } validator.setShouldReport(noTypeCheckSection == 0); break; } } private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, String... arguments) { if (noTypeCheckSection == 0) { t.report(n, diagnosticType, arguments); } } @Override public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { checkNoTypeCheckSection(n, true); switch (n.getType()) { case Token.FUNCTION: // normal type checking final Scope outerScope = t.getScope(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.CAST: Node expr = n.getFirstChild(); ensureTyped(t, n, getJSType(expr)); // If the cast, tightens the type apply it, so it is available post // normalization. JSType castType = getJSType(n); JSType exprType = getJSType(expr); if (castType.isSubtype(exprType)) { expr.setJSType(castType); } break; case Token.NAME: typeable = visitName(t, n, parent); break; case Token.PARAM_LIST: typeable = false; break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: ensureTyped(t, n, NUMBER_TYPE); break; case Token.STRING: ensureTyped(t, n, STRING_TYPE); break; case Token.STRING_KEY: typeable = false; break; case Token.GETTER_DEF:

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: { left = n.getFirstChild(); right = n.getLastChild(); if (left.isTypeOf()) { if (right.isString()) { checkTypeofString(t, right, right.getString()); } } else if (right.isTypeOf() && left.isString()) { checkTypeofString(t, left, left.getString()); } leftType = getJSType(left); rightType = getJSType(right); // We do not want to warn about explicit comparisons to VOID. People // often do this if they think their type annotations screwed up. // // We do want to warn about cases where people compare things like // (Array|null) == (Function|null) // because it probably means they screwed up. // // This heuristic here is not perfect, but should catch cases we // care about without too many false negatives. JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = TernaryValue.UNKNOWN; if (n.getType() == Token.EQ || n.getType() == Token.NE) { result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (n.isNE()) { result = result.not(); } } else { // SHEQ or SHNE if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { result = n.getType() == Token.SHEQ ? TernaryValue.FALSE : TernaryValue.TRUE; } } if (result != TernaryValue.UNKNOWN) { report(t, n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = get

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>UnknownType()) { if (reportUnknownTypes.isOn()) { compiler.report( t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } unknownCount++; } else { typedCount++; } } /** * Visits an assignment <code>lvalue = rvalue</code>. If the * <code>lvalue</code> is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.isAssign()</code> is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); // Check property sets to 'object.property' when 'object' is known. if (lvalue.isGetProp()) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); Node property = lvalue.getLastChild(); String pname = property.getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.isGetProp()) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, pname, lvalue, rvalue); } } checkEnumAlias(t, info, rvalue); checkPropCreation(t, lvalue); // Prototype assignments are special, because they actually affect // the definition of a class. These are mostly validated // during TypedScopeCreator, and we only look for the "dumb" cases here. // object.prototype = ...; if (pname.equals("prototype")) { if (objectJsType != null && objectJsType.isFunctionType()) { FunctionType functionType = objectJsType.toMaybeFunctionType(); if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); // Only assign structs to the prototype of a @struct constructor if (functionType.makesStructs() && !rvalueType.isStruct()) { String funName = functionType.getTypeOfThis().toString(); compiler.report(t.makeError(assign, CONFLICTING_EXTENDED_TYPE, "struct", funName)); } return; } } } // The generic checks for 'object.property' when 'object' is known, // and 'property' is declared on it. // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(pname) && !type.isPropertyTypeInferred(pname) && !propertyIsImplicitCast(type, pname)) { JSType expectedType = type.getPropertyType(pname); if (!expectedType.isUnknownType()) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), expectedType, object, pname); checkPropertyInheritanceOnGetpropAssign( t, assign, object, pname, info, expectedType); return; } } } // If we couldn't get the property type with normal object property // lookups, then check inheritance anyway with the unknown type. checkPropertyInheritanceOnGetpropAssign( t, assign, object, pname, info, getNativeType(UNKNOWN_TYPE)); } // Check qualified name sets to 'object' and 'object.property'. // This can sometimes handle cases when the type of 'object' is not known. // e.g., // var obj = createUnknownType(); // /** @type {number} */ obj.foo = true; JSType leftType = getJSType(lvalue); if (lvalue.isQualifiedName()) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getQualifiedName()); if (var != null) { if (var.isTypeInferred()) { return; } if (NodeUtil.getRootOfQualifiedName(lvalue).isThis

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>() && t.getScope() != var.getScope()) { // Don't look at "this.foo" variables from other scopes. return; } if (var.getType() != null) { leftType = var.getType(); } } } // Fall through case for arbitrary LHS and arbitrary RHS. Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } /** Check that we don't create new properties on structs. */ private void checkPropCreation(NodeTraversal t, Node lvalue) { if (lvalue.isGetProp()) { Node obj = lvalue.getFirstChild(); Node prop = lvalue.getLastChild(); JSType objType = getJSType(obj); String pname = prop.getString(); if (objType.isStruct() && !objType.hasProperty(pname)) { if (!(obj.isThis() && getJSType(t.getScope().getRootNode()).isConstructor())) { report(t, prop, ILLEGAL_PROPERTY_CREATION); } } } } private void checkPropertyInheritanceOnGetpropAssign( NodeTraversal t, Node assign, Node object, String property, JSDocInfo info, JSType propertyType) { // Inheritance checks for prototype properties. // // TODO(nicksantos): This isn't the right place to do this check. We // really want to do this when we're looking at the constructor. // We'd find all its properties and make sure they followed inheritance // rules, like we currently do for @implements to make sure // all the methods are implemented. // // As-is, this misses many other ways to override a property. // // object.prototype.property = ...; if (object.isGetProp()) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = getJSType(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.PARAM_LIST || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); if (childType.isDict()) { report(t, property, TypeValidator.ILLEGAL_PROPERTY_ACCESS, "'.'", "dict"); } else if (validator.expectNotNullOrUndefined(t, n, childType, "No properties on this expression", getNativeType(OBJECT_TYPE))) { checkPropertyAccess(childType, property.getString(), t, n); } ensureTyped(t, n); } /** * Emit a warning if we can prove that a property cannot possibly be * defined on an object. Note the difference between JS and a strictly * statically typed language: we're checking if the property * *cannot be defined*, whereas a java compiler would check if the * property *can be undefined*.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>TestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; case Token.NOT: return parent.getParent().isOr() && parent.getParent().getFirstChild() == parent; } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { validator.expectIndexMatch( t, n, getJSType(n.getFirstChild()), getJSType(n.getLastChild())); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } checkEnumAlias

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(t, info, value); if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); JSType type = getJSType(constructor).restrictByNotNullOrUndefined(); if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null) { visitParameterList(t, n, fnType); ensureTyped(t, n, fnType.getInstanceType()); } else { ensureTyped(t, n); } } else { report(t, n, NOT_A_CONSTRUCTOR); ensureTyped(t, n); } } /** * Check whether there's any property conflict for for a particular super * interface * @param t The node traversal object that supplies context * @param n The node being visited * @param functionName The function name being checked * @param properties The property names in the super interfaces that have * been visited * @param currentProperties The property names in the super interface * that have been visited * @param interfaceType The super interface that is being visited */ private void checkInterfaceConflictProperties(NodeTraversal t, Node n, String functionName, HashMap<String, ObjectType> properties, HashMap<String, ObjectType> currentProperties, ObjectType interfaceType) { Set<String> currentPropertyNames = interfaceType.getImplicitPrototype().getOwnPropertyNames(); for (String name : currentPropertyNames) { ObjectType oType = properties.get(name); if (oType != null) { if (!interfaceType.getPropertyType(name).isEquivalentTo( oType.getPropertyType(name))) { compiler.report( t.makeError(n, INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, functionName, name, oType.toString(), interfaceType.toString())); } } currentProperties.put(name, interfaceType

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>); } for (ObjectType iType : interfaceType.getCtorExtendedInterfaces()) { checkInterfaceConflictProperties(t, n, functionName, properties, currentProperties, iType); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType functionType = JSType.toMaybeFunctionType(n.getJSType()); String functionPrivateName = n.getFirstChild().getString(); if (functionType.isConstructor()) { FunctionType baseConstructor = functionType.getSuperClassConstructor(); if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && baseConstructor != null && baseConstructor.isInterface()) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, "constructor", functionPrivateName)); } else { if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE)) { ObjectType proto = functionType.getPrototype(); if (functionType.makesStructs() && !proto.isStruct()) { compiler.report(t.makeError(n, CONFLICTING_EXTENDED_TYPE, "struct", functionPrivateName)); } else if (functionType.makesDicts() && !proto.isDict()) { compiler.report(t.makeError(n, CONFLICTING_EXTENDED_TYPE, "dict", functionPrivateName)); } } // All interfaces are properly implemented by a class for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj != null) { FunctionType interfaceConstructor = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(!n.isFunction() || type.isFunctionType() || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { // TODO(johnlenz): Change this so that we only look for casts on CAST // nodes one the misplaced type annotation warning is on by default and // people have been given a chance to fix them. As is, this is here // simply for legacy casts. JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry); validator.expectCanCast(t, n, infoType, type); type = infoType; } if (info.isImplicitCast() && !inExterns) { String propName = n.isGetProp() ? n.getLastChild().getString() : "(missing)"; compiler.report( t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName)); } } if (n.getJSType() == null) { n.setJSType(type); } } /** * Returns the percentage of nodes typed by the type checker. * @return a number

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Models an assignment that defines a variable and the removal of it. * */ class DefinitionsRemover { /** * @return an {@link Definition} object if the node contains a definition or * {@code null} otherwise. */ static Definition getDefinition(Node n, boolean isExtern) { // TODO(user): Since we have parent pointers handy. A lot of constructors // can be simplified. // This logic must match #isDefinitionNode Node parent = n.getParent(); if (parent == null) { return null; } if (NodeUtil.isVarDeclaration(n) && n.hasChildren()) { return new VarDefinition(n, isExtern); } else if (parent.isFunction() && parent.getFirstChild() == n) { if (!NodeUtil.isFunctionExpression(parent)) { return new NamedFunctionDefinition(parent, isExtern); } else if (!n.getString().equals("")) { return new FunctionExpressionDefinition(parent, isExtern); } } else if (parent.isAssign() && parent.getFirstChild() == n) { return new AssignmentDefinition(parent, isExtern); } else if (NodeUtil.isObjectLitKey(n, parent)) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> return new ObjectLiteralPropertyDefinition(parent, n, n.getFirstChild(), isExtern); } else if (parent.isParamList()) { Node function = parent.getParent(); return new FunctionArgumentDefinition(function, n, isExtern); } return null; } /** * @return Whether a definition object can be created. */ static boolean isDefinitionNode(Node n) { // This logic must match #getDefinition Node parent = n.getParent(); if (parent == null) { return false; } if (NodeUtil.isVarDeclaration(n) && n.hasChildren()) { return true; } else if (parent.isFunction() && parent.getFirstChild() == n) { if (!NodeUtil.isFunctionExpression(parent)) { return true; } else if (!n.getString().equals("")) { return true; } } else if (parent.isAssign() && parent.getFirstChild() == n) { return true; } else if (NodeUtil.isObjectLitKey(n, parent)) { return true; } else if (parent.isParamList()) { return true; } return false; } static abstract class Definition { private final boolean isExtern; Definition(boolean isExtern) { this.isExtern = isExtern; } /** * Removes this definition from the AST if it is not an extern. * * This method should not be called on a definition for which isExtern() * is true. */ public void remove() { if (!isExtern) { performRemove(); } else { throw new IllegalStateException("Attempt to remove() an extern" + " definition."); } } /** * Subclasses should override to remove the definition from the AST. */ protected abstract void performRemove(); /** * Variable or property name represented by this definition. * For example, in the case of assignments this method would * return the NAME, GETPROP or GETELEM expression that acts as the * assignment left hand side. * * @return the L-Value associated with this definition. * The node's type is always NAME, GETPROP or GETELEM. */ public abstract Node getLValue(); /**

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> * Value expression that acts as the right hand side of the * definition statement. */ public abstract Node getRValue(); /** * Returns true if the definition is an extern. */ public boolean isExtern() { return isExtern; } } /** * Represents an name-only external definition. The definition's * RHS is missing. */ abstract static class IncompleteDefinition extends Definition { private static final Set<Integer> ALLOWED_TYPES = ImmutableSet.of(Token.NAME, Token.GETPROP, Token.GETELEM); private final Node lValue; IncompleteDefinition(Node lValue, boolean inExterns) { super(inExterns); Preconditions.checkNotNull(lValue); Preconditions.checkArgument(ALLOWED_TYPES.contains(lValue.getType()), "Unexpected lValue type %s", Token.name(lValue.getType())); this.lValue = lValue; } @Override public Node getLValue() { return lValue; } @Override public Node getRValue() { return null; } } /** * Represents an unknown definition. */ static final class UnknownDefinition extends IncompleteDefinition { UnknownDefinition(Node lValue, boolean inExterns) { super(lValue, inExterns); } @Override public void performRemove() { throw new IllegalArgumentException("Can't remove an UnknownDefinition"); } } /** * Represents an name-only external definition. The definition's * RHS is missing. */ static final class ExternalNameOnlyDefinition extends IncompleteDefinition { ExternalNameOnlyDefinition(Node lValue) { super(lValue, true); } @Override public void performRemove() { throw new IllegalArgumentException( "Can't remove external name-only definition"); } } /** * Represents a function formal parameter. The definition's RHS is missing. */ static final class FunctionArgumentDefinition extends IncompleteDefinition { FunctionArgumentDefinition(Node function, Node argumentName, boolean inExterns) { super(argumentName, inExterns); Preconditions.checkArgument(function.isFunction()); Preconditions.checkArgument(argumentName.isName()); } @Override public void performRemove()

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> * Example: var x = { e : function() { } }; */ static final class ObjectLiteralPropertyDefinition extends Definition { private final Node literal; private final Node name; private final Node value; ObjectLiteralPropertyDefinition(Node lit, Node name, Node value, boolean isExtern) { super(isExtern); this.literal = lit; this.name = name; this.value = value; } @Override public void performRemove() { literal.removeChild(name); } @Override public Node getLValue() { // TODO(user) revisit: object literal definitions are an example // of definitions whose LHS doesn't correspond to a node that // exists in the AST. We will have to change the return type of // getLValue sooner or later in order to provide this added // flexibility. switch (name.getType()) { case Token.SETTER_DEF: case Token.GETTER_DEF: case Token.STRING_KEY: // TODO(johnlenz): return a GETELEM for quoted strings. return IR.getprop( IR.objectlit(), IR.string(name.getString())); default: throw new IllegalStateException("unexpected"); } } @Override public Node getRValue() { return value; } } /** * Represents a VAR declaration with an assignment. */ static final class VarDefinition extends Definition { private final Node name; VarDefinition(Node node, boolean inExterns) { super(inExterns); Preconditions.checkArgument(NodeUtil.isVarDeclaration(node)); Preconditions.checkArgument(node.hasChildren(), "VAR Declaration of %sshould be assigned a value.", node.getString()); name = node; } @Override public void performRemove() { Node var = name.getParent(); Preconditions.checkState(var.getFirstChild() == var.getLastChild(), "AST should be normalized first"); Node parent = var.getParent(); Node rValue = name.removeFirstChild(); Preconditions.checkState(!parent.isFor()); parent.replaceChild(var, NodeUtil.newExpr(rValue)); } @Override public Node getLValue() { return name; }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.regex.RegExpTree; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable, and checks that regular expressions * are syntactically valid. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( "JSC_MALFORMED_REGEXP", "Malformed Regular Expression: {0}"); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !first))) { t.report(n, REGEXP_REFERENCE); globalRegExpPropertiesUsed = true; } } // Check the syntax of regular expression patterns. } else if (n.isRegExp()) { String pattern = n.getFirstChild().getString(); String flags = n.getChildCount() == 2 ? n.getLastChild().getString() : ""; try { RegExpTree.parseRegExp(pattern, flags); } catch (IllegalArgumentException ex) { t.report(n, MALFORMED_REGEXP, ex.getMessage()); } } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> are valid case com.google.javascript.rhino.head.Token.FUNCTION: FunctionNode fnNode = (FunctionNode)node; valid = fnNode.getFunctionType() == FunctionNode.FUNCTION_STATEMENT; break; // Object literal properties and catch declarations are valid. case com.google.javascript.rhino.head.Token.NAME: valid = node.getParent() instanceof ObjectProperty || node.getParent() instanceof CatchClause || node.getParent() instanceof FunctionNode; break; // Object literal properties are valid case com.google.javascript.rhino.head.Token.GET: case com.google.javascript.rhino.head.Token.SET: case com.google.javascript.rhino.head.Token.NUMBER: case com.google.javascript.rhino.head.Token.STRING: valid = node.getParent() instanceof ObjectProperty; break; // Property assignments are valid, if at the root of an expression. case com.google.javascript.rhino.head.Token.ASSIGN: if (node instanceof Assignment) { valid = isExprStmt(node.getParent()) && isPropAccess(((Assignment)node).getLeft()); } break; // Property definitions are valid, if at the root of an expression. case com.google.javascript.rhino.head.Token.GETPROP: case com.google.javascript.rhino.head.Token.GETELEM: valid = isExprStmt(node.getParent()); break; } if (!valid) { errorReporter.warning(MISPLACED_TYPE_ANNOTATION, sourceName, node.getLineno(), "", 0); } } } private boolean isPropAccess(AstNode node) { return node.getType() == com.google.javascript.rhino.head.Token.GETPROP || node.getType() == com.google.javascript.rhino.head.Token.GETELEM; } private boolean isExprStmt(AstNode node) { return node.getType() == com.google.javascript.rhino.head.Token.EXPR_RESULT || node.getType() == com.google.javascript.rhino.head.Token.EXPR_VOID; } private Node transform(AstNode node

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Node processAstRoot(AstRoot rootNode) { Node node = newNode(Token.SCRIPT); for (com.google.javascript.rhino.head.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * ECMA-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return nType == Token.EXPR_RESULT && n.getFirstChild().isString() && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = newNode(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = newNode(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> nameNode.getLineno(), "", 0); } return newStringNode(Token.NAME, nameNode.getIdentifier()); } } /** * @return Whether the */ private boolean isReservedKeyword(String identifier) { return reservedKeywords != null && reservedKeywords.contains(identifier); } @Override Node processNewExpression(NewExpression exprNode) { Node node = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getTarget())); for (AstNode child : exprNode.getArguments()) { node.addChildToBack(transform(child)); } node.setLineno(exprNode.getLineno()); node.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(node, exprNode); return node; } @Override Node processNumberLiteral(NumberLiteral literalNode) { return newNumberNode(literalNode.getNumber()); } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (config.languageMode == LanguageMode.ECMASCRIPT3) { if (el.isGetter()) { reportGetter(el); continue; } else if (el.isSetter()) { reportSetter(el); continue; } } Node key = transformAsString(el.getLeft()); key.setType(Token.STRING_KEY); Node value = transform(el.getRight()); if (el.isGetter()) { key.setType(Token.GETTER_DEF); Preconditions.checkState(value.isFunction()); if (getFnParamNode(value).hasChildren()) { reportGetterParam(el.getLeft()); } } else if (el.isSetter()) { key.setType(Token.SETTER_DEF); Preconditions.checkState(value.isFunction()); if (!getFnParamNode(value).hasOneChild()) { reportSetterParam(el.getLeft()); } } key.addChildToFront(value); node.addChildToBack(key); }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> return node; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ Node getFnParamNode(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } @Override Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); return node; } @Override Node processPropertyGet(PropertyGet getNode) { Node leftChild = transform(getNode.getTarget()); Node newNode = newNode( Token.GETPROP, leftChild, transformAsString(getNode.getProperty())); newNode.setLineno(leftChild.getLineno()); newNode.setCharno(leftChild.getCharno()); maybeSetLengthFrom(newNode, getNode); return newNode; } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(literalStringNode, literalNode); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(flagsNode, literalNode); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = newNode(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> operand.getLineno(), "", 0); } else if (type == Token.INC || type == Token.DEC) { if (!validAssignmentTarget(operand)) { String msg = (type == Token.INC) ? "invalid increment target" : "invalid decrement target"; errorReporter.error( msg, sourceName, operand.getLineno(), "", 0); } } Node node = newNode(type, operand); if (exprNode.isPostfix()) { node.putBooleanProp(Node.INCRDECR_PROP, true); } return node; } } private boolean validAssignmentTarget(Node target) { switch (target.getType()) { case Token.NAME: case Token.GETPROP: case Token.GETELEM: return true; } return false; } @Override Node processVariableDeclaration(VariableDeclaration declarationNode) { if (!config.acceptConstKeyword && declarationNode.getType() == com.google.javascript.rhino.head.Token.CONST) { processIllegalToken(declarationNode); } Node node = newNode(Token.VAR); for (VariableInitializer child : declarationNode.getVariables()) { node.addChildToBack(transform(child)); } return node; } @Override Node processVariableInitializer(VariableInitializer initializerNode) { Node node = transform(initializerNode.getTarget()); if (initializerNode.getInitializer() != null) { Node initalizer = transform(initializerNode.getInitializer()); node.addChildToBack(initalizer); } return node; } @Override Node processWhileLoop(WhileLoop loopNode) { return newNode( Token.WHILE, transform(loopNode.getCondition()), transformBlock(loopNode.getBody())); } @Override Node processWithStatement(WithStatement statementNode) { return newNode( Token.WITH, transform(statementNode.getExpression()), transformBlock(statementNode.getStatement())); } @Override Node processIllegalToken(AstNode node) { errorReporter.error( "Unsupported syntax: " + com.google.javascript.rhino.head.Token.typeToName( node.getType()), sourceName,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>LSH; case com.google.javascript.rhino.head.Token.RSH: return Token.RSH; case com.google.javascript.rhino.head.Token.URSH: return Token.URSH; case com.google.javascript.rhino.head.Token.ADD: return Token.ADD; case com.google.javascript.rhino.head.Token.SUB: return Token.SUB; case com.google.javascript.rhino.head.Token.MUL: return Token.MUL; case com.google.javascript.rhino.head.Token.DIV: return Token.DIV; case com.google.javascript.rhino.head.Token.MOD: return Token.MOD; case com.google.javascript.rhino.head.Token.NOT: return Token.NOT; case com.google.javascript.rhino.head.Token.BITNOT: return Token.BITNOT; case com.google.javascript.rhino.head.Token.POS: return Token.POS; case com.google.javascript.rhino.head.Token.NEG: return Token.NEG; case com.google.javascript.rhino.head.Token.NEW: return Token.NEW; case com.google.javascript.rhino.head.Token.DELPROP: return Token.DELPROP; case com.google.javascript.rhino.head.Token.TYPEOF: return Token.TYPEOF; case com.google.javascript.rhino.head.Token.GETPROP: return Token.GETPROP; case com.google.javascript.rhino.head.Token.GETELEM: return Token.GETELEM; case com.google.javascript.rhino.head.Token.CALL: return Token.CALL; case com.google.javascript.rhino.head.Token.NAME: return Token.NAME; case com.google.javascript.rhino.head.Token.NUMBER: return Token.NUMBER; case com.google.javascript.rhino.head.Token.STRING: return Token.STRING; case com.google.javascript.rhino.head.Token.NULL: return Token.NULL; case com.google.javascript.rh

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; /** * Value types (null, void, number, boolean, string). */ abstract class ValueType extends JSType { ValueType(JSTypeRegistry registry) { super(registry); } @Override final JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } @Override public

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.getGrandparent(); if (grandparent.isName() && grandparent.getString() == v.name) { continue; } // Only generate warnings if the scopes do not match in order // to deal with possible forward declarations and recursion if (reference.getScope() == v.scope) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, UNDECLARED_REFERENCE, v.name)); } } } if (isDeclaration) { blocksWithDeclarations.add(basicBlock); isDeclaredInScope = true; } } } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>toObjectType(); FunctionType thatCtor = thatObj == null ? null : thatObj.getConstructor(); if (getConstructor() != null && getConstructor().isInterface()) { for (ObjectType thisInterface : getCtorExtendedInterfaces()) { if (thisInterface.isSubtype(that)) { return true; } } } else if (thatCtor != null && thatCtor.isInterface()) { Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces(); for (ObjectType thisInterface : thisInterfaces) { if (thisInterface.isSubtype(that)) { return true; } } } // other prototype based objects if (isUnknownType() || implicitPrototypeChainIsUnknown()) { // If unsure, say 'yes', to avoid spurious warnings. // TODO(user): resolve the prototype chain completely in all cases, // to avoid guessing. return true; } return thatObj != null && isImplicitPrototype(thatObj); } private boolean implicitPrototypeChainIsUnknown() { ObjectType p = getImplicitPrototype(); while (p != null) { if (p.isUnknownType()) { return true; } p = p.getImplicitPrototype(); } return false; } @Override public boolean hasCachedValues() { return super.hasCachedValues(); } /** Whether this is a built-in object. */ @Override public boolean isNativeObjectType() { return nativeType; } @Override void setOwnerFunction(FunctionType type) { Preconditions.checkState(ownerFunction == null || type == null); ownerFunction = type; } @Override public FunctionType getOwnerFunction() { return ownerFunction; } @Override public Iterable<ObjectType> getCtorImplementedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getImplementedInterfaces() : ImmutableList.<ObjectType>of(); } @Override public Iterable<ObjectType> getCtorExtendedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getExtendedInterfaces() : ImmutableList.<ObjectType>of(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype !=

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> null) { implicitPrototypeFallback = (ObjectType) implicitPrototype.resolve(t, scope); } for (Property prop : properties.values()) { prop.setType(safeResolve(prop.getType(), t, scope)); } return this; } @Override public void matchConstraint(JSType constraint) { // We only want to match constraints on anonymous types. if (hasReferenceName()) { return; } // Handle the case where the constraint object is a record type. // // param constraint {{prop: (number|undefined)}} // function f(constraint) {} // f({}); // // We want to modify the object literal to match the constraint, by // taking any each property on the record and trying to match // properties on this object. if (constraint.isRecordType()) { matchRecordTypeConstraint(constraint.toObjectType()); } else if (constraint.isUnionType()) { for (JSType alt : constraint.toMaybeUnionType().getAlternates()) { if (alt.isRecordType()) { matchRecordTypeConstraint(alt.toObjectType()); } } } } public void matchRecordTypeConstraint(ObjectType constraintObj) { for (String prop : constraintObj.getOwnPropertyNames()) { JSType propType = constraintObj.getPropertyType(prop); if (!isPropertyTypeDeclared(prop)) { JSType typeToInfer = propType; if (!hasProperty(prop)) { typeToInfer = getNativeType(JSTypeNative.VOID_TYPE) .getLeastSupertype(propType); } defineInferredProperty(prop, typeToInfer, null); } } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Scopes(); symbolTable.fillThisReferences(this, externsRoot, jsRoot); symbolTable.fillPropertySymbols(this, externsRoot, jsRoot); symbolTable.fillJSDocInfo(this, externsRoot, jsRoot); return symbolTable; } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); } abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { if (typeValidator == null) { typeValidator = new TypeValidator(this); } return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main JS sources. jsRoot = IR.block(); jsRoot.setIsSyntheticBlock(true); externsRoot = IR.block(); externsRoot.setIsSyntheticBlock(true); externAndJsRoot = IR.block(externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer("parseInputs"); try { // Parse externs sources. for (CompilerInput input : externs) { Node n = input.getAst

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(firstChild); } else { parent.addChildrenAfter( firstChild, injectedLibraries.get("base")); } reportCodeChange(); injectedLibraries.put(resourceName, lastChild); return lastChild; } /** Load a library as a resource */ @VisibleForTesting Node loadLibraryCode(String resourceName) { String originalCode; try { originalCode = CharStreams.toString(new InputStreamReader( Compiler.class.getResourceAsStream( String.format("js/%s.js", resourceName)), Charsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } return Normalize.parseAndNormalizeSyntheticCode( this, originalCode, String.format("jscomp_%s_", resourceName)); } /** Returns the compiler version baked into the jar. */ public static String getReleaseVersion() { ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); return config.getString("compiler.version"); } /** Returns the compiler date baked into the jar. */ public static String getReleaseDate() { ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); return config.getString("compiler.date"); } /** * Stores the old parse tree for a given source file. * @param sourceName * @param oldAst */ public void setOldParseTree(String sourceName, AstRoot oldAst) { } /** * Gets an old format parse tree for a given source file. * @param sourceName * @return */ public AstRoot getOldParseTreeByName(String sourceName) { return null; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> return getPropertyMap().getSlot(name); } @Override public Property getOwnSlot(String name) { return getPropertyMap().getOwnProperty(name); } @Override public JSType getTypeOfThis() { return null; } /** * Gets the declared default element type. * @see ParameterizedType */ public JSType getParameterType() { return null; } /** * Gets the declared default index type. * @see IndexedType */ public JSType getIndexType() { return null; } /** * Gets the docInfo for this type. */ @Override public JSDocInfo getJSDocInfo() { if (docInfo != null) { return docInfo; } else if (getImplicitPrototype() != null) { return getImplicitPrototype().getJSDocInfo(); } else { return super.getJSDocInfo(); } } /** * Sets the docInfo for this type from the given * {@link JSDocInfo}. The {@code JSDocInfo} may be {@code null}. */ public void setJSDocInfo(JSDocInfo info) { docInfo = info; } /** * Detects a cycle in the implicit prototype chain. This method accesses * the {@link #getImplicitPrototype()} method and must therefore be * invoked only after the object is sufficiently initialized to respond to * calls to this method.<p> * * @return True iff an implicit prototype cycle was detected. */ final boolean detectImplicitPrototypeCycle() { // detecting cycle this.visited = true; ObjectType p = getImplicitPrototype(); while (p != null) { if (p.visited) { return true; } else { p.visited = true; } p = p.getImplicitPrototype(); } // clean up p = this; do { p.visited = false; p = p.getImplicitPrototype(); } while (p != null); return false; } /** * Detects cycles in either the implicit prototype chain, or the implemented/extended * interfaces.<p> * * @return True iff a cycle was detected. */ final boolean detectInheritanceCycle() { //

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> getPropertyNode(String propertyName) { Property p = getSlot(propertyName); return p == null ? null : p.getNode(); } /** * Gets the docInfo on the specified property on this type. This should not * be implemented recursively, as you generally need to know exactly on * which type in the prototype chain the JSDocInfo exists. */ public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { Property p = getOwnSlot(propertyName); return p == null ? null : p.getJSDocInfo(); } /** * Sets the docInfo for the specified property from the * {@link JSDocInfo} on its definition. * @param info {@code JSDocInfo} for the property definition. May be * {@code null}. */ public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { // by default, do nothing } @Override public JSType findPropertyType(String propertyName) { return hasProperty(propertyName) ? getPropertyType(propertyName) : null; } /** * Gets the property type of the property whose name is given. If the * underlying object does not have this property, the Unknown type is * returned to indicate that no information is available on this property. * * This gets overridden by FunctionType for lazily-resolved call() and * bind() functions. * * @return the property's type or {@link UnknownType}. This method never * returns {@code null}. */ public JSType getPropertyType(String propertyName) { StaticSlot<JSType> slot = getSlot(propertyName); if (slot == null) { if (isNoResolvedType() || isCheckedUnknownType()) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } else if (isEmptyType()) { return getNativeType(JSTypeNative.NO_TYPE); } return getNativeType(JSTypeNative.UNKNOWN_TYPE); } return slot.getType(); } @Override public boolean hasProperty(String propertyName) { // Unknown types have all properties. return isEmptyType() || isUnknownType() || getSlot(propertyName) != null; } /** * Checks whether the property whose name

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> is given is present directly on * the object. Returns false even if it is declared on a supertype. */ public boolean hasOwnProperty(String propertyName) { return getOwnSlot(propertyName) != null; } /** * Returns the names of all the properties directly on this type. * * Overridden by FunctionType to add "prototype". */ public Set<String> getOwnPropertyNames() { return getPropertyMap().getOwnPropertyNames(); } /** * Checks whether the property's type is inferred. */ public boolean isPropertyTypeInferred(String propertyName) { StaticSlot<JSType> slot = getSlot(propertyName); return slot == null ? false : slot.isTypeInferred(); } /** * Checks whether the property's type is declared. */ public boolean isPropertyTypeDeclared(String propertyName) { StaticSlot<JSType> slot = getSlot(propertyName); return slot == null ? false : !slot.isTypeInferred(); } /** * Whether the given property is declared on this object. */ final boolean hasOwnDeclaredProperty(String name) { return hasOwnProperty(name) && isPropertyTypeDeclared(name); } /** Checks whether the property was defined in the externs. */ public boolean isPropertyInExterns(String propertyName) { Property p = getSlot(propertyName); return p == null ? false : p.isFromExterns(); } /** * Gets the number of properties of this object. */ public int getPropertiesCount() { return getPropertyMap().getPropertiesCount(); } /** * Returns a list of properties defined or inferred on this type and any of * its supertypes. */ public Set<String> getPropertyNames() { Set<String> props = Sets.newTreeSet(); collectPropertyNames(props); return props; } /** * Adds any properties defined on this type or its supertypes to the set. */ final void collectPropertyNames(Set<String> props) { getPropertyMap().collectPropertyNames(props); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseObjectType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Token.THIS: case Token.TRUE: validateChildless(n); return; // General unary ops case Token.DELPROP: case Token.POS: case Token.NEG: case Token.NOT: case Token.INC: case Token.DEC: case Token.TYPEOF: case Token.VOID: case Token.BITNOT: case Token.CAST: validateUnaryOp(n); return; // General binary ops case Token.COMMA: case Token.OR: case Token.AND: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.ADD: case Token.MUL: case Token.MOD: case Token.DIV: validateBinaryOp(n); return; // Assignments case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: validateAssignmentExpression(n); return; case Token.HOOK: validateTrinaryOp(n); return; // Node types that require special handling case Token.STRING: validateString(n); return; case Token.NUMBER: validateNumber(n); return; case Token.NAME: validateName(n); return; case Token.GETELEM: validateBinaryOp(n); return; case Token.GETPROP: validateGetProp(n); return; case Token.ARRAYLIT: validateArrayLit(n); return; case Token.OBJECTLIT:

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> validateObjectLit(n); return; case Token.REGEXP: validateRegExpLit(n); return; case Token.CALL: validateCall(n); return; case Token.NEW: validateNew(n); return; case Token.FUNCTION: validateFunctionExpression(n); return; default: violation("Expected expression but was " + Token.name(n.getType()), n); } } private void validateBlock(Node n) { validateNodeType(Token.BLOCK, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } private void validateSyntheticBlock(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } private void validateIsSynthetic(Node n) { if (!n.getBooleanProp(Node.SYNTHETIC_BLOCK_PROP)) { violation("Missing 'synthetic block' annotation.", n); } } private void validateHasSourceName(Node n) { String sourceName = n.getSourceFileName(); if (sourceName == null || sourceName.isEmpty()) { violation("Missing 'source name' annotation.", n); } } private void validateHasInputId(Node n) { InputId inputId = n.getInputId(); if (inputId == null) { violation("Missing 'input id' annotation.", n); } } private void validateLabel(Node n) { validateNodeType(Token.LABEL, n); validateChildCount(n, 2); validateLabelName(n.getFirstChild()); validateStatement(n.getLastChild()); } private void validateLabelName(Node n) { validateNodeType(Token.LABEL_NAME, n); validateNonEmptyString(n); validateChildCount(n, 0); } private void validateNonEmptyString(Node n) { validateNonNullString(n); if (n.getString().isEmpty()) { violation("Expected non-empty string.", n); } } private void validateNonNullString(Node n) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> if (n.getString() == null) { violation("Expected non-null string.", n); } } private void validateName(Node n) { validateNodeType(Token.NAME, n); validateNonEmptyString(n); validateChildCount(n, 0); } private void validateOptionalName(Node n) { validateNodeType(Token.NAME, n); validateNonNullString(n); validateChildCount(n, 0); } private void validateFunctionStatement(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n, 3); validateName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); } private void validateFunctionExpression(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n, 3); validateOptionalName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); } private void validateParameters(Node n) { validateNodeType(Token.PARAM_LIST, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateName(c); } } private void validateCall(Node n) { validateNodeType(Token.CALL, n); validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateExpression(c); } } private void validateNew(Node n) { validateNodeType(Token.NEW, n); validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateExpression(c); } } private void validateVar(Node n) { validateNodeType(Token.VAR, n); this.validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // Don't use the validateName here as the NAME is allowed to have // a child. validateNodeType(Token.NAME, c); validateNonEmptyString(c); validateMaximumChildCount(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(n.getLastChild()); } private void validateSwitch(Node n) { validateNodeType(Token.SWITCH, n); validateMinimumChildCount(n, 1); validateExpression(n.getFirstChild()); int defaults = 0; for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) { validateSwitchMember(n.getLastChild()); if (c.isDefaultCase()) { defaults++; } } if (defaults > 1) { violation("Expected at most 1 'default' in switch but was " + defaults, n); } } private void validateSwitchMember(Node n) { switch (n.getType()) { case Token.CASE: validateCase(n); return; case Token.DEFAULT_CASE: validateDefault(n); return; default: violation("Expected switch member but was " + Token.name(n.getType()), n); } } private void validateDefault(Node n) { validateNodeType(Token.DEFAULT_CASE, n); validateChildCount(n, 1); validateSyntheticBlock(n.getLastChild()); } private void validateCase(Node n) { validateNodeType(Token.CASE, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateSyntheticBlock(n.getLastChild()); } private void validateOptionalExpression(Node n) { if (n.isEmpty()) { validateChildless(n); } else { validateExpression(n); } } private void validateChildless(Node n) { validateChildCount(n, 0); } private void validateAssignmentExpression(Node n) { validateChildCount(n, 2); validateAssignmentTarget(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateAssignmentTarget(Node n) { switch (n.getType()) { case Token.NAME: case Token.GETELEM: case Token.GETPROP: validateExpression(n); return; default: violation("Expected assignment target expression but was " + Token.name(n.getType()), n); } } private void validateGetProp(Node n)

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> { validateNodeType(Token.GETPROP, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); Node prop = n.getLastChild(); validateNodeType(Token.STRING, prop); validateNonEmptyString(prop); } private void validateRegExpLit(Node n) { validateNodeType(Token.REGEXP, n); validateMinimumChildCount(n, 1); validateMaximumChildCount(n, 2); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateString(c); } } private void validateString(Node n) { validateNodeType(Token.STRING, n); validateChildCount(n, 0); try { // Validate that getString doesn't throw n.getString(); } catch (UnsupportedOperationException e) { violation("Invalid STRING node.", n); } } private void validateNumber(Node n) { validateNodeType(Token.NUMBER, n); validateChildCount(n, 0); try { // Validate that getDouble doesn't throw n.getDouble(); } catch (UnsupportedOperationException e) { violation("Invalid NUMBER node.", n); } } private void validateArrayLit(Node n) { validateNodeType(Token.ARRAYLIT, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // EMPTY is allowed to represent empty slots. validateOptionalExpression(c); } } private void validateObjectLit(Node n) { validateNodeType(Token.OBJECTLIT, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateObjectLitKey(c); } } private void validateObjectLitKey(Node n) { switch (n.getType()) { case Token.GETTER_DEF: validateObjectLitGetKey(n); return; case Token.SETTER_DEF: validateObjectLitSetKey(n); return; case Token.STRING_KEY: validateObjectLitStringKey(n); return; default: violation("Expected object literal key expression but was " + Token.name(n.getType()), n); } } private void validateObject

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>LitGetKey(Node n) { validateNodeType(Token.GETTER_DEF, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit get functions must be nameless, and must have zero parameters. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (functionParams.hasChildren()) { violation("get methods must not have parameters.", n); } } private void validateObjectLitSetKey(Node n) { validateNodeType(Token.SETTER_DEF, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit set functions must be nameless, and must have 1 parameter. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (!functionParams.hasOneChild()) { violation("set methods must have exactly one parameter.", n); } } private void validateObjectLitStringKey(Node n) { validateNodeType(Token.STRING_KEY, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); validateExpression(n.getFirstChild()); } private void validateObjectLiteralKeyName(Node n) { if (n.isQuotedString()) { try { // Validate that getString doesn't throw n.getString(); } catch (UnsupportedOperationException e) { violation("getString failed for" + Token.name(n.getType()), n); } } else { validateNonEmptyString(n); } } private void validateUnaryOp(Node n) { validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateBinaryOp(Node n) { validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateTrinaryOp(Node n) { validateChildCount(n, 3);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * <p>The syntactic scope creator scans the parse tree to create a Scope object * containing all the variable declarations in that scope.</p> * * <p>This implementation is not thread-safe.</p> * */ class SyntacticScopeCreator implements ScopeCreator { private final AbstractCompiler compiler; private Scope scope; private InputId inputId; private final RedeclarationHandler redeclarationHandler; // The arguments variable is special, in that it's declared in every local // scope, but not explicitly declared. private static final String ARGUMENTS = "arguments"; public static final DiagnosticType VAR_MULTIPLY_DECLARED_ERROR = DiagnosticType.error( "JSC_VAR_MULTIPLY_DECLARED_ERROR", "Variable {0} first declared in {1}"); public static final DiagnosticType VAR_ARGUMENTS_SHADOWED_ERROR = DiagnosticType.error( "JSC_VAR_ARGUMENTS_SHADOWED_ERROR", "Shadowing \"arguments\" is not allowed"); /** * Creates a ScopeCreator. */ SyntacticScopeCreator(AbstractCompiler compiler) { this.compiler = compiler; this.redeclarationHandler = new DefaultRedeclarationHandler(); } SyntacticScopeCreator( Abstract

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Compiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } @Override public Scope createScope(Node n, Scope parent) { inputId = null; if (parent == null) { scope = Scope.createGlobalScope(n); } else { scope = new Scope(parent, n); } scanRoot(n, parent); inputId = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top-level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.isCatch() && parent.isCatch()) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); if

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> (!allowDupe) { compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_ARGUMENTS_SHADOWED_ERROR)); } } } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { Preconditions.checkState(n.isName()); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, input); } else { scope.declare(name, n, null, input); } } /** * @param n The name node to check. * @param origVar The associated Var. * @return Whether duplicated declarations warnings should be suppressed * for the given node. */ static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Node origParent = origVar.getParentNode(); JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } if (info != null && info.getSuppressions().contains("duplicate")) { return true; } info = origVar.nameNode.getJSDocInfo(); if (info == null) { info = origParent.getJSDocInfo(); } return (info != null && info.getSuppressions().contains("duplicate")); } /** * Generates an untyped global scope from the root

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> of AST of compiler (which * includes externs). * * @param compiler The compiler for which the scope is generated. * @return The new untyped global scope generated as a result of this call. */ static Scope generateUntypedTopScope(AbstractCompiler compiler) { return new SyntacticScopeCreator(compiler).createScope(compiler.getRoot(), null); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> to our definition of scope.</li> * <li>Exported variables as they can be needed after the script terminates. * </li> * <li>Names of named functions because in JavaScript, <i>function foo(){}</i> * does not kill <i>foo</i> in the dataflow.</li> */ static void computeEscaped(final Scope jsScope, final Set<Var> escaped, AbstractCompiler compiler) { // TODO(user): Very good place to store this information somewhere. AbstractPostOrderCallback finder = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (jsScope == t.getScope() || !n.isName() || parent.isFunction()) { return; } String name = n.getString(); Var var = t.getScope().getVar(name); if (var != null && var.scope == jsScope) { escaped.add(jsScope.getVar(name)); } } }; NodeTraversal t = new NodeTraversal(compiler, finder); t.traverseAtScope(jsScope); // 1: Remove the exception name in CATCH which technically isn't local to // begin with. for (Iterator<Var> i = jsScope.getVars(); i.hasNext();) { Var var = i.next(); if (var.getParentNode().isCatch() || compiler.getCodingConvention().isExported(var.getName())) { escaped.add(var); } } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return false; } } /** Checks that the given name is used legally. */ private void checkNameUse(NodeTraversal t, Node n) { Var v = t.getScope().getVar(n.getString()); if (v == null) { // In particular, this prevents creating a global variable by assigning // to it without a declaration. if (!noVarCheck) { t.report(n, UNKNOWN_VARIABLE, n.getString()); } } if (!noCajaChecks) { if ("eval".equals(n.getString())) { t.report(n, EVAL_USE); } else if (n.getString().endsWith("__")) { t.report(n, ILLEGAL_NAME); } } } /** Checks that an assignment is not to the "arguments" object. */ private void checkAssignment(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that name is illegal. if (noCajaChecks) { t.report(n, EVAL_ASSIGNMENT); } } } } /** Checks that variables, functions, and arguments are not deleted. */ private void checkDelete(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { Var v = t.getScope().getVar(n.getFirstChild().getString()); if (v != null) { t.report(n, DELETE_VARIABLE); } } } /** Checks that object literal keys are valid. */ private void checkObjectLiteral(NodeTraversal t, Node n) { Set

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS><String> getters = Sets.newHashSet(); Set<String> setters = Sets.newHashSet(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!noCajaChecks && key.getString().endsWith("__")) { t.report(key, ILLEGAL_NAME); } if (!key.isSetterDef()) { // normal property and getter cases if (getters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { getters.add(key.getString()); } } if (!key.isGetterDef()) { // normal property and setter cases if (setters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { setters.add(key.getString()); } } } } /** Checks that label names are valid. */ private void checkLabel(NodeTraversal t, Node n) { if (n.getFirstChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getFirstChild(), ILLEGAL_NAME); } } } /** Checks that are performed on non-extern code only. */ private class NonExternChecks extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if ((n.isName()) && isDeclaration(n)) { checkDeclaration(t, n); } else if (n.isGetProp()) { checkProperty(t, n); } } /** Checks for illegal declarations. */ private void checkDeclaration(NodeTraversal t, Node n) { if ("eval".equals(n.getString())) { t.report(n, EVAL_DECLARATION); } else if ("arguments".equals(n.getString())) { t.report(n, ARGUMENTS_DECLARATION); } else if (n.getString().endsWith("__")) { if (!noCajaChecks) { t.report(n, ILLEGAL_NAME); } } } /** Checks for illegal property accesses. */ private void checkProperty(NodeTraversal t, Node n

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) { if (n.getLastChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getLastChild(), ILLEGAL_NAME); } } } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>can.c. */ public final static int ERROR = -1, RETURN = 4, BITOR = 9, BITXOR = 10, BITAND = 11, EQ = 12, NE = 13, LT = 14, LE = 15, GT = 16, GE = 17, LSH = 18, RSH = 19, URSH = 20, ADD = 21, SUB = 22, MUL = 23, DIV = 24, MOD = 25, NOT = 26, BITNOT = 27, POS = 28, NEG = 29, NEW = 30, DELPROP = 31, TYPEOF = 32, GETPROP = 33, GETELEM = 35, CALL = 37, NAME = 38, NUMBER = 39, STRING = 40, NULL = 41, THIS = 42, FALSE = 43, TRUE = 44, SHEQ = 45, // shallow equality (===) SHNE = 46, // shallow inequality (!==) REGEXP = 47, THROW = 49, IN = 51, INSTANCEOF = 52, ARRAYLIT = 63, // array literal OBJECTLIT = 64, // object literal TRY = 77, PARAM_LIST = 83, COMMA = 85, // comma operator ASSIGN = 86, // simple assignment (=) ASSIGN_BITOR = 87, // |= ASSIGN_BITXOR = 88, // ^= ASSIGN_BITAND = 89, // &= ASSIGN_LSH = 90, // <<=

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>SDoc-only tokens ANNOTATION = 300, PIPE = 301, STAR = 302, EOC = 303, QMARK = 304, ELLIPSIS = 305, BANG = 306, EQUALS = 307, LB = 308, // left brackets LC = 309, // left curly braces COLON = 310; // Transitional definitions // TODO(johnlenz): remove these public final static int DEFAULT = DEFAULT_CASE, GET = GETTER_DEF, LP = PARAM_LIST, SET = SETTER_DEF; public static String name(int token) { switch (token) { case ERROR: return "ERROR"; case RETURN: return "RETURN"; case BITOR: return "BITOR"; case BITXOR: return "BITXOR"; case BITAND: return "BITAND"; case EQ: return "EQ"; case NE: return "NE"; case LT: return "LT"; case LE: return "LE"; case GT: return "GT"; case GE: return "GE"; case LSH: return "LSH"; case RSH: return "RSH"; case URSH: return "URSH"; case ADD: return "ADD"; case SUB: return "SUB"; case MUL: return "MUL"; case DIV: return "DIV"; case MOD: return "MOD"; case NOT: return "NOT"; case BITNOT: return "BITNOT"; case POS: return "POS"; case NEG: return "NEG"; case NEW: return "NEW"; case DELPROP: return "DELPROP"; case TYPEOF: return "TYPEOF"; case GETPROP: return "GETPROP"; case GETELEM: return "GETELEM"; case CALL: return "CALL"; case NAME: return "NAME"; case LABEL_NAME: return "LABEL_NAME"; case NUMBER: return

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>URE) ? CheckLevel.ERROR : CheckLevel.WARNING; // TODO(anatol): add flag that decides whether to process UNNAMED messages. // Some projects would not want such functionality (unnamed) as they don't // use SOY templates. } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) { compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(), checkLevel, MESSAGE_NODE_IS_ORPHANED)); } } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { String messageKey; boolean isVar; Node msgNode, msgNodeParent; switch (node.getType()) { case Token.NAME: // var MSG_HELLO = 'Message' if ((parent != null) && (parent.isVar())) { messageKey = node.getString(); isVar = true; } else { return; } msgNode = node.getFirstChild(); msgNodeParent = node; break; case Token.ASSIGN: // somenamespace.someclass.MSG_HELLO = 'Message' isVar = false; Node getProp = node.getFirstChild(); if (!getProp.isGetProp()) { return; } Node propNode = getProp.getLastChild(); messageKey = propNode.getString(); msgNode = node.getLastChild(); msgNodeParent = node; break; case Token.CALL: // goog.getMsg() String fnName = node.getFirstChild().getQualifiedName(); if (MSG_FUNCTION_NAME.equals(fnName)) { googMsgNodes.put(node, traversal.getSourceName()); } else if (MSG_FALLBACK_FUNCTION_NAME.equals(fnName)) { visitFallbackFunctionCall(traversal, node); } return; default: return; } // Is this a message name? boolean isNewStyleMessage = msgNode != null && msgNode.isCall(); if (!isMessageName(messageKey, isNewStyleMessage)) { return; } if

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(traversal.makeError(node, MESSAGE_HAS_NO_DESCRIPTION, messageKey)); } JsMessageDefinition msgDefinition = new JsMessageDefinition( node, msgNode, msgNodeParent); processJsMessage(extractedMessage, msgDefinition); } /** * Track a message for later retrieval. * * This is used for tracking duplicates, and for figuring out message * fallback. Not all message types are trackable, because that would * require a more sophisticated analysis. e.g., * function f(s) { s.MSG_UNNAMED_X = 'Some untrackable message'; } */ private void trackMessage( NodeTraversal t, JsMessage message, String msgName, Node msgNode, boolean isUnnamedMessage) { if (!isUnnamedMessage) { MessageLocation location = new MessageLocation(message, msgNode); messageNames.put(msgName, location); } else if (msgNode.isName()) { Var var = t.getScope().getVar(msgName); if (var != null) { unnamedMessages.put(var, message); } } } /** Get a previously tracked message. */ private JsMessage getTrackedMessage(NodeTraversal t, String msgName) { boolean isUnnamedMessage = isUnnamedMessageName(msgName); if (!isUnnamedMessage) { MessageLocation location = messageNames.get(msgName); return location == null ? null : location.message; } else { Var var = t.getScope().getVar(msgName); if (var != null) { return unnamedMessages.get(var); } } return null; } /** * Checks if message already processed. If so - it generates 'message * duplicated' compiler error. * * @param msgName the name of the message * @param msgNode the node that represents JS message */ private void checkIfMessageDuplicated(String msgName, Node msgNode) { if (messageNames.containsKey(msgName)) { MessageLocation location = messageNames.get(msgName); compiler.report(JSError.make(msgNode, MESSAGE_DUPLICATE_KEY, msgName, location.messageNode.getSourceFileName(), Integer.toString(location.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> correspond to a valid JS message node */ private void extractMessageFromProperty( Builder builder, Node getPropNode, Node assignNode) throws MalformedException { Node callNode = getPropNode.getNext(); maybeInitMetaDataFromJsDoc(builder, assignNode); extractFromCallNode(builder, callNode); } /** * Initializes the meta data in a JsMessage by examining the nodes just before * and after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param varNode the message VAR node * @param parentOfVarNode {@code varNode}'s parent node */ private void maybeInitMetaDataFromJsDocOrHelpVar( Builder builder, Node varNode, @Nullable Node parentOfVarNode) throws MalformedException { // First check description in @desc if (maybeInitMetaDataFromJsDoc(builder, varNode)) { return; } // Check the preceding node for meta data if ((parentOfVarNode != null) && maybeInitMetaDataFromHelpVar(builder, parentOfVarNode.getChildBefore(varNode))) { return; } // Check the subsequent node for meta data maybeInitMetaDataFromHelpVar(builder, varNode.getNext()); } /** * Initializes the meta data in a JsMessage by examining a node just before or * after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param sibling a node adjacent to the message VAR node * @return true iff message has corresponding description variable */ private boolean maybeInitMetaDataFromHelpVar(Builder builder, @Nullable Node sibling) throws MalformedException { if ((sibling != null) && (sibling.isVar())) { Node nameNode = sibling.getFirstChild(); String name = nameNode.getString(); if (name.equals(builder.getKey() + DESC_SUFFIX)) { Node valueNode = nameNode.getFirstChild(); String desc = extractStringFromStringExprNode(valueNode); if (desc.startsWith(HIDDEN_DESC_PREFIX)) { builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim()); builder.setIsHidden(true); } else { builder.setDesc(desc);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } return true; } } return false; } /** * Initializes the meta data in a message builder given a node that may * contain JsDoc properties. * * @param builder the message builder whose meta data will be initialized * @param node the node with the message's JSDoc properties * @return true if message has JsDoc with valid description in @desc * annotation */ private boolean maybeInitMetaDataFromJsDoc(Builder builder, Node node) { boolean messageHasDesc = false; JSDocInfo info = node.getJSDocInfo(); if (info != null) { String desc = info.getDescription(); if (desc != null) { builder.setDesc(desc); messageHasDesc = true; } if (info.isHidden()) { builder.setIsHidden(true); } if (info.getMeaning() != null) { builder.setMeaning(info.getMeaning()); } } return messageHasDesc; } /** * Returns the string value associated with a node representing a JS string or * several JS strings added together (e.g. {@code 'str'} or {@code 's' + 't' + * 'r'}). * * @param node the node from where we extract the string * @return String representation of the node * @throws MalformedException if the parsed message is invalid */ private static String extractStringFromStringExprNode(Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: return node.getString(); case Token.ADD: StringBuilder sb = new StringBuilder(); for (Node child : node.children()) { sb.append(extractStringFromStringExprNode(child)); } return sb.toString(); default: throw new MalformedException("STRING or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a FUNCTION node. * <p> * <pre> * The tree should look something like: * * function * |-- name * |-- lp * | |-- name <arg1> * | -- name <arg2>

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> * -- block * | * --return * | * --add * |-- string foo * -- name <arg1> * </pre> * * @param builder the message builder * @param node the function node that contains a message * @throws MalformedException if the parsed message is invalid */ private void extractFromFunctionNode(Builder builder, Node node) throws MalformedException { Set<String> phNames = Sets.newHashSet(); for (Node fnChild : node.children()) { switch (fnChild.getType()) { case Token.NAME: // This is okay. The function has a name, but it is empty. break; case Token.PARAM_LIST: // Parse the placeholder names from the function argument list. for (Node argumentNode : fnChild.children()) { if (argumentNode.isName()) { String phName = argumentNode.getString(); if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, argumentNode); } else { phNames.add(phName); } } } break; case Token.BLOCK: // Build the message's value by examining the return statement Node returnNode = fnChild.getFirstChild(); if (!returnNode.isReturn()) { throw new MalformedException("RETURN node expected; found: " + getReadableTokenName(returnNode), returnNode); } for (Node child : returnNode.children()) { extractFromReturnDescendant(builder, child); } // Check that all placeholders from the message text have appropriate // object literal keys for (String phName : builder.getPlaceholders()) { if(!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, returnNode); } } break; default: throw new MalformedException( "NAME, LP, or BLOCK node expected; found: " + getReadableTokenName(node), fnChild); } } } /** * Appends value parts to the message builder by traversing the descendants * of the given RETURN node. * * @param

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> builder the message builder * @param node the node from where we extract a message * @throws MalformedException if the parsed message is invalid */ private void extractFromReturnDescendant(Builder builder, Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: builder.appendStringPart(node.getString()); break; case Token.NAME: builder.appendPlaceholderReference(node.getString()); break; case Token.ADD: for (Node child : node.children()) { extractFromReturnDescendant(builder, child); } break; default: throw new MalformedException( "STRING, NAME, or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a CALL node. * <p> * The tree should look something like: * * <pre> * call * |-- getprop * | |-- name 'goog' * | +-- string 'getMsg' * | * |-- string 'Hi {$userName}! Welcome to {$product}.' * +-- objlit * |-- string 'userName' * |-- name 'someUserName' * |-- string 'product' * +-- call * +-- name 'getProductName' * </pre> * * @param builder the message builder * @param node the call node from where we extract the message * @throws MalformedException if the parsed message is invalid */ private void extractFromCallNode(Builder builder, Node node) throws MalformedException { // Check the function being called if (!node.isCall()) { throw new MalformedException( "Message must be initialized using " + MSG_FUNCTION_NAME + " function.", node); } Node fnNameNode = node.getFirstChild(); if (!MSG_FUNCTION_NAME.equals(fnNameNode.getQualifiedName())) { throw new MalformedException( "Message initialized using unrecognized function. " + "Please use " + MSG_FUNCTION_NAME + "() instead.", fnNameNode); } // Get the message string Node stringLiteralNode = fnNameNode.getNext(); if (stringLiteral

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Node == null) { throw new MalformedException("Message string literal expected", stringLiteralNode); } // Parse the message string and append parts to the builder parseMessageTextNode(builder, stringLiteralNode); Node objLitNode = stringLiteralNode.getNext(); Set<String> phNames = Sets.newHashSet(); if (objLitNode != null) { // Register the placeholder names if (!objLitNode.isObjectLit()) { throw new MalformedException("OBJLIT node expected", objLitNode); } for (Node aNode = objLitNode.getFirstChild(); aNode != null; aNode = aNode.getNext()) { if (!aNode.isStringKey()) { throw new MalformedException("STRING_KEY node expected as OBJLIT key", aNode); } String phName = aNode.getString(); if (!isLowerCamelCaseWithNumericSuffixes(phName)) { throw new MalformedException( "Placeholder name not in lowerCamelCase: " + phName, aNode); } if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, aNode); } phNames.add(phName); } } // Check that all placeholders from the message text have appropriate objlit // values Set<String> usedPlaceholders = builder.getPlaceholders(); for (String phName : usedPlaceholders) { if(!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, objLitNode); } } // Check that objLiteral have only names that are present in the // message text for (String phName : phNames) { if(!usedPlaceholders.contains(phName)) { throw new MalformedException( "Unused message placeholder: " + phName, objLitNode); } } } /** * Appends the message parts in a JS message value extracted from the given * text node. * * @param builder the JS message builder to append parts to * @param node the node with string literal that contains the message text * @throws MalformedException if {@code value} contains a reference to * an

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> unregistered placeholder */ private void parseMessageTextNode(Builder builder, Node node) throws MalformedException { String value = extractStringFromStringExprNode(node); while(true) { int phBegin = value.indexOf(PH_JS_PREFIX); if (phBegin < 0) { // Just a string literal builder.appendStringPart(value); return; } else { if (phBegin > 0) { // A string literal followed by a placeholder builder.appendStringPart(value.substring(0, phBegin)); } // A placeholder. Find where it ends int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin); if (phEnd < 0) { throw new MalformedException( "Placeholder incorrectly formatted in: " + builder.getKey(), node); } String phName = value.substring(phBegin + PH_JS_PREFIX.length(), phEnd); builder.appendPlaceholderReference(phName); int nextPos = phEnd + PH_JS_SUFFIX.length(); if (nextPos < value.length()) { // Iterate on the rest of the message value value = value.substring(nextPos); } else { // The message is parsed return; } } } } /** Visit a call to goog.getMsgWithFallback. */ private void visitFallbackFunctionCall(NodeTraversal t, Node call) { // Check to make sure the function call looks like: // goog.getMsgWithFallback(MSG_1, MSG_2); if (call.getChildCount() != 3 || !call.getChildAtIndex(1).isName() || !call.getChildAtIndex(2).isName()) { compiler.report(t.makeError(call, BAD_FALLBACK_SYNTAX)); return; } Node firstArg = call.getChildAtIndex(1); JsMessage firstMessage = getTrackedMessage(t, firstArg.getString()); if (firstMessage == null) { compiler.report( t.makeError(firstArg, FALLBACK_ARG_ERROR, firstArg.getString())); return; } Node secondArg = firstArg.getNext(); JsMessage secondMessage = getTrackedMessage( t, call.getChild

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>AtIndex(2).getString()); if (secondMessage == null) { compiler.report( t.makeError(secondArg, FALLBACK_ARG_ERROR, secondArg.getString())); return; } processMessageFallback(call, firstMessage, secondMessage); } /** * Processes found JS message. Several examples of "standard" processing * routines are: * <ol> * <li>extract all JS messages * <li>replace JS messages with localized versions for some specific language * <li>check that messages have correct syntax and present in localization * bundle * </ol> * * @param message the found message * @param definition the definition of the object and usually contains all * additional message information like message node/parent's node */ abstract void processJsMessage(JsMessage message, JsMessageDefinition definition); /** * Processes the goog.getMsgWithFallback primitive. * goog.getMsgWithFallback(MSG_1, MSG_2); * * By default, does nothing. */ void processMessageFallback(Node callNode, JsMessage message1, JsMessage message2) {} /** * Returns whether the given JS identifier is a valid JS message name. */ boolean isMessageName(String identifier, boolean isNewStyleMessage) { return identifier.startsWith(MSG_PREFIX) && (style == JsMessage.Style.CLOSURE || isNewStyleMessage || !identifier.endsWith(DESC_SUFFIX)); } /** * Returns whether the given message name is in the unnamed namespace. */ private static boolean isUnnamedMessageName(String identifier) { return MSG_UNNAMED_PATTERN.matcher(identifier).matches(); } /** * Returns whether a string is nonempty, begins with a lowercase letter, and * contains only digits and underscores after the first underscore. */ static boolean isLowerCamelCaseWithNumericSuffixes(String input) { return CAMELCASE_PATTERN.matcher(input).matches(); } /** * Returns human-readable name of the given node's type. */ private static String getReadableTokenName(Node node) { return Token.name(node.getType()); } /** * Converts the given string from upper-underscore case to

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Type system, or the concrete type system. */ private DisambiguateProperties(AbstractCompiler compiler, TypeSystem<T> typeSystem, Map<String, CheckLevel> propertiesToErrorFor) { this.compiler = compiler; this.typeSystem = typeSystem; this.propertiesToErrorFor = propertiesToErrorFor; if (!this.propertiesToErrorFor.isEmpty()) { this.invalidationMap = LinkedHashMultimap.create(); } else { this.invalidationMap = null; } } @Override public void process(Node externs, Node root) { Preconditions.checkState( compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED); for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA, mis.src); addInvalidatingType(mis.typeB, mis.src); } StaticScope<T> scope = typeSystem.getRootScope(); NodeTraversal.traverse(compiler, externs, new FindExternProperties()); NodeTraversal.traverse(compiler, root, new FindRenameableProperties()); renameProperties(); } private void recordInvalidationError(JSType t, JSError error) { if (!t.isObject()) { return; } if (invalidationMap != null) { invalidationMap.put(t, error); } } /** * Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type, JSError error) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt, error); } } else if (type.isEnumElementType()) { addInvalidatingType( type.toMaybeEnumElementType().getPrimitiveType(), error); } else { typeSystem.addInvalidatingType(type); recordInvalidationError(type, error); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getImplicitPrototype() != null) { typeSystem.addInvalidatingType(objType.getImplicitPrototype()); recordInvalid

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>ationError(objType.getImplicitPrototype(), error); } } } /** Returns the property for the given name, creating it if necessary. */ protected Property getProperty(String name) { if (!properties.containsKey(name)) { properties.put(name, new Property(name)); } return properties.get(name); } /** Public for testing. */ T getTypeWithProperty(String field, T type) { return typeSystem.getTypeWithProperty(field, type); } /** Tracks the current type system scope while traversing. */ private abstract class AbstractScopingCallback implements ScopedCallback { protected final Stack<StaticScope<T>> scopes = new Stack<StaticScope<T>>(); @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { scopes.push(typeSystem.getRootScope()); } else { scopes.push(typeSystem.getFunctionScope(t.getScopeRoot())); } } @Override public void exitScope(NodeTraversal t) { scopes.pop(); } /** Returns the current scope at this point in the file. */ protected StaticScope<T> getScope() { return scopes.peek(); } } /** * Finds all properties defined in the externs file and sets them as * ineligible for renaming from the type on which they are defined. */ private class FindExternProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // TODO(johnlenz): Support object-literal property definitions. if (n.isGetProp()) { String field = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), field); Property prop = getProperty(field); if (typeSystem.isInvalidatingType(type)) { prop.invalidate(); } else { prop.addTypeToSkip(type); // If this is a prototype property, then we want to skip assignments // to the instance type as well. These assignments are not usually // seen in the extern code itself, so we

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> must handle them here. if ((type = typeSystem.getInstanceFromPrototype(type)) != null) { prop.getTypes().add(type); prop.typesToSkip.add(type); } } } } } /** * Traverses the tree, building a map from field names to Nodes for all * fields that can be renamed. */ private class FindRenameableProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { handleGetProp(t, n); } else if (n.isObjectLit()) { handleObjectLit(t, n); } } /** * Processes a GETPROP node. */ private void handleGetProp(NodeTraversal t, Node n) { String name = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), name); Property prop = getProperty(name); if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) { if (propertiesToErrorFor.containsKey(name)) { String suggestion = ""; if (type instanceof JSType) { JSType jsType = (JSType) type; if (jsType.isAllType() || jsType.isUnknownType()) { if (n.getFirstChild().isThis()) { suggestion = "The \"this\" object is unknown in the function,"+ "consider using @this"; } else { String qName = n.getFirstChild().getQualifiedName(); suggestion = "Consider casting " + qName + " if you know it's type."; } } else { List<String> errors = Lists.newArrayList(); printErrorLocations(errors, jsType); if (!errors.isEmpty()) { suggestion = "Consider fixing errors for the following types:\n"; suggestion += Joiner.on("\n").join(errors); } } } compiler.report(JSError.make( t.getSourceName(), n, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), suggestion));

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } } } /** * Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { Node child = n.getFirstChild(); while (child != null) { // Maybe STRING, GET, SET // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { // TODO(user): It doesn't look like the user can do much in this // case right now. if (propertiesToErrorFor.containsKey(name)) { compiler.report(JSError.make( t.getSourceName(), child, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), "")); } } child = child.getNext(); } } private void printErrorLocations(List<String> errors, JSType t) { if (!t.isObject() || t.isAllType()) { return; } if (t.isUnionType()) { for (JSType alt : t.toMaybeUnionType().getAlternates()) { printErrorLocations(errors, alt); } return; } for (JSError error : invalidationMap.get(t)) { if (errors.size() > MAX_INVALDIATION_WARNINGS_PER_PROPERTY) { return; } errors.add( t.toString() + " at " + error.sourceName + ":" + error.lineNumber); } } /** * Processes a property, adding it to the list of properties to rename. * @return a representative type for the property reference, which will be * the highest type on the prototype chain of the provided type. In the * case of a union type, it will be the highest type on the prototype * chain of one of the members of the union. */ private T processProperty( NodeTraversal t, Property prop, T type, T relatedType) { type = typeSystem.restrictByNotNullOr

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> * @param prop Only types with this property need to be returned. In general * with type tightening, this will require no special processing, but in * the case of an unknown JSType, we might need to add in the native * types since we don't track them, but only if they have the given * property. */ T getType(StaticScope<T> scope, Node node, String prop); /** * Returns true if a field reference on this type will invalidate all * references to that field as candidates for renaming. This is true if the * type is unknown or all-inclusive, as variables with such a type could be * references to any object. */ boolean isInvalidatingType(T type); /** * Informs the given type system that a type is invalidating due to a type * mismatch found during type checking. */ void addInvalidatingType(JSType type); /** * Returns a set of types that should be skipped given the given type. * This is necessary for interfaces when using JSTypes, as all super * interfaces must also be skipped. */ ImmutableSet<T> getTypesToSkipForType(T type); /** * Determines whether the given type is one whose properties should not be * considered for renaming. */ boolean isTypeToSkip(T type); /** Remove null and undefined from the options in the given type. */ T restrictByNotNullOrUndefined(T type); /** * Returns the alternatives if this is a type that represents multiple * types, and null if not. Union and interface types can correspond to * multiple other types. */ Iterable<T> getTypeAlternatives(T type); /** * Returns the type in the chain from the given type that contains the given * field or null if it is not found anywhere. */ T getTypeWithProperty(String field, T type); /** * Returns the type of the instance of which this is the prototype or null * if this is not a function prototype. */ T getInstanceFromPrototype(T type); /** * Records that this property could be referenced from any interface that * this type, or any type in its superclass chain, implements. */ void recordInterfaces(T type, T relatedType, DisambiguateProperties<T

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>>.Property p); } /** Implementation of TypeSystem using JSTypes. */ private static class JSTypeSystem implements TypeSystem<JSType> { private final Set<JSType> invalidatingTypes; private JSTypeRegistry registry; public JSTypeSystem(AbstractCompiler compiler) { registry = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope<JSType> getRootScope() { return null; } @Override public StaticScope<JSType> getFunctionScope(Node node) { return null; } @Override public JSType getType( StaticScope<JSType> scope, Node node, String prop) { if (node.getJSType() == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return node.getJSType(); } @Override public boolean isInvalidatingType(JSType type) { if (type == null || invalidatingTypes.contains(type) || type.isUnknownType() /* unresolved types */) { return true; } ObjectType objType = ObjectType.cast(type); return objType != null && !objType.hasReferenceName(); } @Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { Set<JSType> types = Sets.newHashSet(type); for (JSType alt : type.toMaybeUnionType().get

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Scope<ConcreteType> scope, Node node, String prop) { if (scope != null) { ConcreteType c = tt.inferConcreteType( (TightenTypes.ConcreteScope) scope, node); return maybeAddAutoboxes(c, node, prop); } else { return null; } } /** * Add concrete types for autoboxing types if necessary. The concrete type * system does not track native types, like string, so add them if they are * present in the JSType for the node. */ private ConcreteType maybeAddAutoboxes( ConcreteType cType, Node node, String prop) { JSType jsType = node.getJSType(); if (jsType == null) { return cType; } else if (jsType.isUnknownType()) { for (JSTypeNative nativeType : nativeTypes) { ConcreteType concrete = tt.getConcreteInstance( tt.getTypeRegistry().getNativeObjectType(nativeType)); if (concrete != null && !concrete.getPropertyType(prop).isNone()) { cType = cType.unionWith(concrete); } } return cType; } return maybeAddAutoboxes(cType, jsType, prop); } private ConcreteType maybeAddAutoboxes( ConcreteType cType, JSType jsType, String prop) { jsType = jsType.restrictByNotNullOrUndefined(); if (jsType.isUnionType()) { for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { cType = maybeAddAutoboxes(cType, alt, prop); } return cType; } else if (jsType.isEnumElementType()) { return maybeAddAutoboxes( cType, jsType.toMaybeEnumElementType().getPrimitiveType(), prop); } if (jsType.autoboxesTo() != null) { JSType autoboxed = jsType.autoboxesTo(); return cType.unionWith(tt.getConcreteInstance((ObjectType) autoboxed)); } else if (jsType.unboxesTo() != null) { return cType.unionWith(tt.getConcreteInstance((ObjectType) jsType)); } return cType; } @Override public boolean isInvalidatingType(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>doc = NodeUtil.getBestJSDocInfo(node); if (jsdoc == null && !node.isFunction()) { return ""; } JSType type = node.getJSType(); if (type == null) { return ""; } else if (type.isFunctionType()) { return getFunctionAnnotation(node); } else if (type.isEnumType()) { return "/** @enum {" + type.toMaybeEnumType().getElementsType().toAnnotationString() + "} */\n"; } else if (!type.isUnknownType() && !type.isEmptyType() && !type.isVoidType() && !type.isFunctionPrototypeType()) { return "/** @type {" + node.getJSType().toAnnotationString() + "} */\n"; } else { return ""; } } /** * @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.isFunction()); StringBuilder sb = new StringBuilder("/**\n"); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = type.toMaybeFunctionType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * "); appendAnnotation(sb, "param", getParameterNodeJSDocType(n)); sb.append(" ") .append(paramNode.getString()) .append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> we model scopes but handles some // additional cases that are not handled by the current Scope object. // Specifically, a Scope currently has only two concepts of scope (global, // and function local). But there are in reality a couple of additional // case to worry about: // catch expressions // function expressions names // Both belong to a scope by themselves. private Deque<Renamer> nameStack = new ArrayDeque<Renamer>(); private final Renamer rootRenamer; MakeDeclaredNamesUnique() { this(new ContextualRenamer()); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used, the starting context can not // be a function. Preconditions.checkState( !declarationRoot.isFunction() || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (!declarationRoot.isFunction()) { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null &&

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> !NodeUtil.isFunctionDeclaration(n)) { renamer.addDeclaredName(name); } nameStack.push(renamer); } break; case Token.PARAM_LIST: { Renamer renamer = nameStack.peek().forChildScope(); // Add the function parameters for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = n.getNext(); findDeclaredNames(functionBody, null, renamer); nameStack.push(renamer); } break; case Token.CATCH: { Renamer renamer = nameStack.peek().forChildScope(); String name = n.getFirstChild().getString(); renamer.addDeclaredName(name); nameStack.push(renamer); } break; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: String newName = getReplacementName(n.getString()); if (newName != null) { Renamer renamer = nameStack.peek(); if (renamer.stripConstIfReplaced()) { // TODO(johnlenz): Do we need to do anything about the Javadoc? n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); t.getCompiler().reportCodeChange(); } break; case Token.FUNCTION: // Remove the function body scope nameStack.pop(); // Remove function recursive name (if any). nameStack.pop(); break; case Token.PARAM_LIST: // Note: The parameters and function body variables live in the // same scope, we introduce the scope when in the "shouldTraverse" // visit of LP, but remove it when when we exit the function above. break; case Token.CATCH: // Remove catch except name from the stack of names. nameStack.pop(); break; } } /** * Walks the stack of name maps and finds the replacement name for the * current scope

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>. */ private String getReplacementName(String oldName) { for (Renamer names : nameStack) { String newName = names.getReplacementName(oldName); if (newName != null) { return newName; } } return null; } /** * Traverses the current scope and collects declared names. Does not * decent into functions or add CATCH exceptions. */ private void findDeclaredNames(Node n, Node parent, Renamer renamer) { // Do a shallow traversal, so don't traverse into function declarations, // except for the name of the function itself. if (parent == null || !parent.isFunction() || n == parent.getFirstChild()) { if (NodeUtil.isVarDeclaration(n)) { renamer.addDeclaredName(n.getString()); } else if (NodeUtil.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); renamer.addDeclaredName(nameNode.getString()); } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { findDeclaredNames(c, n, renamer); } } } /** * Declared names renaming policy interface. */ interface Renamer { /** * Called when a declared name is found in the local current scope. */ void addDeclaredName(String name); /** * @return A replacement name, null if oldName is unknown or should not * be replaced. */ String getReplacementName(String oldName); /** * @return Whether the constant-ness of a name should be removed. */ boolean stripConstIfReplaced(); /** * @return A Renamer for a scope within the scope of the current Renamer. */ Renamer forChildScope(); } /** * Inverts the transformation by {@link ContextualRenamer}, when possible. */ static class ContextualRenameInverter implements ScopedCallback, CompilerPass { private final AbstractCompiler compiler; // The set of names referenced in the current scope. private Set<String> referencedNames = ImmutableSet.of(); // Stack reference sets. private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>();

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>containsSeparator(name) && !getOrginalName(name).isEmpty()) { String newName = findReplacementName(name); referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List<Node> references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.isName()); n.setString(newName); } compiler.reportCodeChange(); nameMap.remove(name); } } /** * Find a name usable in the local scope. */ private String findReplacementName(String name) { String original = getOrginalName(name); String newName = original; int i = 0; while (!isValidName(newName)) { newName = original + ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++); } return newName; } /** * @return Whether the name is valid to use in the local scope. */ private boolean isValidName(String name) { if (TokenStream.isJSIdentifier(name) && !referencedNames.contains(name) && !name.equals(ARGUMENTS)) { return true; } return false; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node node, Node parent) { if (t.inGlobalScope()) { return; } if (NodeUtil.isReferenceName(node)) { String name = node.getString(); // Add all referenced names to the set so it is possible to check for // conflicts. referencedNames.add(name); // Store only references to candidate names in the node map. if (containsSeparator(name)) { addCandidateNameReference(name, node); } } } private void addCandidateNameReference(String name, Node n) { List<Node> nodes = nameMap.get(name); if (null == nodes) { nodes = Lists.newLinkedList(); nameMap.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>( propertyName, type, inferred, propertyNode); } @Override public boolean removeProperty(String name) { return referencedObjType == null ? false : referencedObjType.removeProperty(name); } @Override public JSType findPropertyType(String propertyName) { return referencedType.findPropertyType(propertyName); } @Override public JSDocInfo getJSDocInfo() { return referencedType.getJSDocInfo(); } @Override public void setJSDocInfo(JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setJSDocInfo(info); } } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setPropertyJSDocInfo(propertyName, info); } } @Override public FunctionType getConstructor() { return referencedObjType == null ? null : referencedObjType.getConstructor(); } @Override public JSType getParameterType() { return referencedObjType == null ? null : referencedObjType.getParameterType(); } @Override public JSType getIndexType() { return referencedObjType == null ? null : referencedObjType.getIndexType(); } @Override public <T> T visit(Visitor<T> visitor) { return referencedType.visit(visitor); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return referencedType.visit(visitor, that); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setReferencedType(referencedType.resolve(t, scope)); return this; } @Override public String toDebugHashCodeString() { return "{proxy:" + referencedType.toDebugHashCodeString() + "}"; } @Override public JSType getTypeOfThis() { if (referencedObjType != null) { return referencedObjType.getTypeOfThis(); } return super.getTypeOfThis(); } @Override public JSType collapseUnion() { if (referencedType.isUnionType()) { return referencedType.collapseUnion(); } return this; } @Override public void matchConstraint(JS

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> != null && lValueType.isNominalConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return (lValueType.toMaybeFunctionType()).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.isFunction() || parent.isVar() || parent.isNew()) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.isNew()) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) { Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE) { StaticSourceFile varSrc = var.getSourceFile(); StaticSourceFile refSrc = name.getStaticSourceFile(); if (varSrc != null && refSrc != null && !varSrc.getName().equals(refSrc.getName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), varSrc.getName())); } } } } } /** * Determines whether the given property with @const tag got reassigned * @param t The current traversal. * @param getprop The getprop node. */ private void checkConstantProperty(NodeTraversal t, Node getprop) { // Check whether the property is modified Node parent = getprop.getParent(); boolean isDelete = parent.isDelProp(); if (!(NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == getprop) && !parent.isInc() && !parent.isDec() && !isDelete) { return; } ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); boolean

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> isConstant = isPropertyDeclaredConstant(objectType, propertyName); // Check whether constant properties are reassigned if (isConstant) { if (isDelete) { compiler.report( t.makeError(getprop, CONST_PROPERTY_DELETED, propertyName)); return; } ObjectType oType = objectType; while (oType != null) { if (oType.hasReferenceName()) { if (initializedConstantProperties.containsEntry( oType.getReferenceName(), propertyName)) { compiler.report( t.makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE, propertyName)); break; } } oType = oType.getImplicitPrototype(); } Preconditions.checkState(objectType.hasReferenceName()); initializedConstantProperties.put(objectType.getReferenceName(), propertyName); // Add the prototype when we're looking at an instance object if (objectType.isInstanceType()) { ObjectType prototype = objectType.getImplicitPrototype(); if (prototype != null) { if (prototype.hasProperty(propertyName) && prototype.hasReferenceName()) { initializedConstantProperties.put(prototype.getReferenceName(), propertyName); } } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = parent.getJSDocInfo() != null && parent.isAssign() && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>FromNode(Node n) { Node newParam = newParameterFromNode(n); if (!newParam.isVarArgs() && !newParam.isOptionalArg()) { newParam.setOptionalArg(true); } return newParam; } // Add a parameter to the list with the given type. private Node newParameter(JSType type) { Node paramNode = Node.newString(Token.NAME, ""); paramNode.setJSType(type); root.addChildToBack(paramNode); return paramNode; } public Node build() { return root; } private boolean hasOptionalOrVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && (lastChild.isOptionalArg() || lastChild.isVarArgs()); } public boolean hasVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && lastChild.isVarArgs(); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> @param callNode A CALL node. */ public String getSingletonGetterClassName(Node callNode); /** * In many JS libraries, the function that adds a singleton getter to a class * adds properties to the class. */ public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType); /** * @return Whether the function is inlinable by convention. */ public boolean isInlinableFunction(Node n); /** * @return the delegate relationship created by the call or null. */ public DelegateRelationship getDelegateRelationship(Node callNode); /** * In many JS libraries, the function that creates a delegate relationship * also adds properties to the delegator and delegate base. */ public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate); /** * @return the name of the delegate superclass. */ public String getDelegateSuperclassName(); /** * Checks for function calls that set the calling conventions on delegate * methods. */ public void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions); /** * Defines the delegate proxy prototype properties. Their types depend on * properties of the delegate base methods. * * @param delegateProxyPrototypes List of delegate proxy prototypes. */ public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope<JSType> scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions); /** * Gets the name of the global object. */ public String getGlobalObject(); /** * A Bind instance or null. */ public Bind describeFunctionBind(Node n); /** * A Bind instance or null. * @param useTypeInfo If we believe type information is reliable enough * to use to figure out what the bind function is. */ public Bind describeFunctionBind(Node n, boolean useTypeInfo); public static class Bind { // The target of the bind action final Node target; // The node representing the "this" value, maybe null final Node thisValue; // The head of a Node list representing the parameters final Node parameters; public Bind(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public TypePair getTypesUnderShallowInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseUnionType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseUnionType(this, that); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); // for circularly defined types. boolean changed = false; ImmutableList.Builder<JSType> resolvedTypes = ImmutableList.builder(); for (JSType alternate : alternates) { JSType newAlternate = alternate.resolve(t, scope); changed |= (alternate != newAlternate); resolvedTypes.add(alternate); } if (changed) { Collection<JSType> newAlternates = resolvedTypes.build(); Preconditions.checkState( newAlternates.hashCode() == this.hashcode); alternates = newAlternates; } return this; } @Override public String toDebugHashCodeString() { List<String> hashCodes = Lists.newArrayList(); for (JSType a : alternates) { hashCodes.add(a.toDebugHashCodeString()); } return "{(" + Joiner.on(",").join(hashCodes) + ")}"; } @Override

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNoObjectType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseNoObjectType(that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "NoObject"; } @Override public FunctionType getConstructor() { return null; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> : -1L; } if (1 <= c && c <= 9) { long v = c; for (int i = 1; i != len; ++i) { c = str.charAt(i) - '0'; if (!(0 <= c && c <= 9)) { return -1; } v = 10 * v + c; } // Check for overflow if ((v >>> 32) == 0) { return v; } } } return -1; } static boolean isSpecialProperty(String s) { return s.equals("__proto__") || s.equals("__parent__"); } // ------------------ // Statements // ------------------ public static String getMessage0(String messageId) { return getMessage(messageId, null); } public static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } /* OPT there's a noticeable delay for the first error! Maybe it'd * make sense to use a ListResourceBundle instead of a properties * file to avoid (synchronized) text parsing. */ public static String getMessage(String messageId, Object[] arguments) { final String defaultResource = "rhino_ast.java.com.google.javascript.rhino.Messages"; Locale locale = Locale.getDefault(); // ResourceBundle does caching. ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale); String formatString; try { formatString = rb.getString(messageId); } catch (java.util.MissingResourceException mre) { throw new RuntimeException ("no message resource found for message property "+ messageId); } /* * It's OK to format the string, even if 'arguments' is null; * we need to format it anyway, to make double ''s collapse to * single 's. */ // TODO: MessageFormat is not available on pJava MessageFormat formatter = new MessageFormat(formatString); return formatter.format(arguments); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>} if JSDoc information was correctly parsed, * {@code false} otherwise */ boolean parse() { int lineno; int charno; // JSTypes are represented as Rhino AST nodes, and then resolved later. JSTypeExpression type; state = State.SEARCHING_ANNOTATION; skipEOLs(); JsDocToken token = next(); List<ExtendedTypeInfo> extendedTypes = Lists.newArrayList(); // Always record that we have a comment. if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo blockInfo = extractBlockComment(token); token = blockInfo.token; if (!blockInfo.string.isEmpty()) { jsdocBuilder.recordBlockDescription(blockInfo.string); } } else { if (token != JsDocToken.ANNOTATION && token != JsDocToken.EOC) { // Mark that there was a description, but don't bother marking // what it was. jsdocBuilder.recordBlockDescription(""); } } // Parse the actual JsDoc. retry: for (;;) { switch (token) { case ANNOTATION: if (state == State.SEARCHING_ANNOTATION) { state = State.SEARCHING_NEWLINE; lineno = stream.getLineno(); charno = stream.getCharno(); String annotationName = stream.getString(); Annotation annotation = annotationNames.get(annotationName); if (annotation == null) { parser.addParserWarning("msg.bad.jsdoc.tag", annotationName, stream.getLineno(), stream.getCharno()); } else { // Mark the beginning of the annotation. jsdocBuilder.markAnnotation(annotationName, lineno, charno); switch (annotation) { case AUTHOR: if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo authorInfo = extractSingleLineBlock(); String author = authorInfo.string; if (author.length() == 0) { parser.addParserWarning("msg.jsdoc.authormissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addAuthor(author); } token = authorInfo.token; } else { token = eatTokensUntilEOL

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> if (matchingRc) { if (token != JsDocToken.RC) { parser.addTypeWarning("msg.jsdoc.missing.rc", stream.getLineno(), stream.getCharno()); } } else if (token != JsDocToken.EOL && token != JsDocToken.EOF && token != JsDocToken.EOC) { parser.addTypeWarning("msg.end.annotation.expected", stream.getLineno(), stream.getCharno()); } } else { parser.addTypeWarning("msg.no.type.name", lineno, charno); } token = eatTokensUntilEOL(token); continue retry; case HIDDEN: if (!jsdocBuilder.recordHiddenness()) { parser.addParserWarning("msg.jsdoc.hidden", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case LENDS: skipEOLs(); matchingRc = false; if (match(JsDocToken.LC)) { token = next(); matchingRc = true; } if (match(JsDocToken.STRING)) { token = next(); if (!jsdocBuilder.recordLends(stream.getString())) { parser.addTypeWarning("msg.jsdoc.lends.incompatible", stream.getLineno(), stream.getCharno()); } } else { parser.addTypeWarning("msg.jsdoc.lends.missing", stream.getLineno(), stream.getCharno()); } if (matchingRc && !match(JsDocToken.RC)) { parser.addTypeWarning("msg.jsdoc.missing.rc", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case MEANING: ExtractionInfo meaningInfo = extractMultilineTextualBlock(token); String meaning = meaningInfo.string; token = meaningInfo.token; if (!jsdocBuilder.recordMeaning(meaning)) { parser.addParserWarning("msg.jsdoc.meaning.extra", stream.getLineno(), stream.getCharno()); } continue retry; case NO_ALIAS

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> case PARAM: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordParamTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); } String name = null; boolean isBracketedParam = JsDocToken.LB == token; if (isBracketedParam) { token = next(); } if (JsDocToken.STRING != token) { parser.addTypeWarning("msg.missing.variable.name", lineno, charno); } else { name = stream.getString(); if (isBracketedParam) { token = next(); // Throw out JsDocToolkit's "default" parameter // annotation. It makes no sense under our type // system. if (JsDocToken.EQUALS == token) { token = next(); if (JsDocToken.STRING == token) { token = next(); } } if (JsDocToken.RB != token) { reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } else if (type != null) { // Make the type expression optional, if it isn't // already. type = JSTypeExpression.makeOptionalArg(type); } } // If the param name has a DOT in it, just throw it out // quietly. We do not handle the JsDocToolkit method // for handling properties of params. if (name.indexOf('.') > -1) { name = null; } else if (!jsdocBuilder.recordParameter(name, type)) { if (jsdocBuilder.hasParameter(name)) { parser.addTypeWarning("msg.dup.variable.name", name, lineno, charno); } else { parser.addTypeWarning("msg.jsdoc.incompat.type", name, lineno, charno); } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>SEARCHING_NEWLINE) { state = State.SEARCHING_ANNOTATION; } token = next(); continue retry; default: if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) { token = next(); continue retry; } else { state = State.SEARCHING_NEWLINE; token = eatTokensUntilEOL(); continue retry; } } // next token token = next(); } } private void checkExtendedTypes(List<ExtendedTypeInfo> extendedTypes) { for (ExtendedTypeInfo typeInfo : extendedTypes) { // If interface, record the multiple extended interfaces if (jsdocBuilder.isInterfaceRecorded()) { if (!jsdocBuilder.recordExtendedInterface(typeInfo.type)) { parser.addParserWarning("msg.jsdoc.extends.duplicate", typeInfo.lineno, typeInfo.charno); } } else { if (!jsdocBuilder.recordBaseType(typeInfo.type)) { parser.addTypeWarning("msg.jsdoc.incompat.type", typeInfo.lineno, typeInfo.charno); } } } } /** * Parse a {@code @suppress} tag of the form * {@code @suppress&#123;warning1|warning2&#125;}. * * @param token The current token. */ private JsDocToken parseSuppressTag(JsDocToken token) { if (token == JsDocToken.LC) { Set<String> suppressions = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!suppressionNames.contains(name)) { parser.addParserWarning("msg.jsdoc.suppress.unknown", name, stream.getLineno(), stream.getCharno()); } suppressions.add(stream.getString()); token = next(); } else { parser.addParserWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordSuppressions(suppressions)) { parser.addParserWarning("msg.jsdoc.suppress.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Parse a {@code @modifies} tag of the form * {@code @modifies&#123;this|arguments|param&#125;}. * * @param token The current token. */ private JsDocToken parseModifiesTag(JsDocToken token) { if (token == JsDocToken.LC) { Set<String> modifies = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!modifiesAnnotationKeywords.contains(name) && !jsdocBuilder.hasParameter(name)) { parser.addParserWarning("msg.jsdoc.modifies.unknown", name, stream.getLineno(), stream.getCharno()); } modifies.add(stream.getString()); token = next(); } else { parser.addParserWarning("msg.jsdoc.modifies", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.modifies", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordModifies(modifies)) { parser.addParserWarning("msg.jsdoc.modifies.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> endLineno = stream.getLineno(); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endLineno, endCharno, true); } return typeNode; } /** * Looks for a parameter type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parseTypeNameAnnotation(token); } else { typeNode = parseTypeExpressionAnnotation(token); } if (typeNode != null) { int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode( typeNode, lineno, startCharno, endLineno, endCharno, matchingLC); } return typeNode; } /** * Converts a JSDoc token to its string representation. */ private String toString(JsDocToken token) { switch (token) { case ANNOTATION: return "@" + stream.getString(); case BANG: return "!"; case COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case ST

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>AR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ private JSTypeExpression createJSTypeExpression(Node n) { return n == null ? null : new JSTypeExpression(n, getSourceName()); } /** * Tuple for returning both the string extracted and the * new token following a call to any of the extract*Block * methods. */ private static class ExtractionInfo { private final String string; private final JsDocToken token; public ExtractionInfo(String string, JsDocToken token) { this.string = string; this.token = token; } } /** * Tuple for recording extended types */ private static class ExtendedTypeInfo { final JSTypeExpression type; final int lineno; final int charno; public ExtendedTypeInfo(JSTypeExpression type, int lineno, int charno) { this.type = type; this.lineno = lineno; this.charno = charno; } } /** * Extracts the text found on the current line starting at token. Note that * token = token.info; should be called after this method is used to update * the token properly in the parser. * * @return The extraction information. */ private ExtractionInfo extractSingleLineBlock() { // Get the current starting point. stream.update(); int lineno = stream.getLineno(); int charno = stream.getCharno() + 1; String line = stream.getRemainingJSDocLine().trim(); // Record the textual description. if (line.length() > 0) { jsdocBuilder.markText(line, lineno, charno, lineno, charno + line.length()); } return new ExtractionInfo(line, next()); } private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) { return extractMultilineTextualBlock(token, WhitespaceOption.SINGLE_LINE); } private enum Wh

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.LB) { skipEOLs(); return parseArrayType(next()); } else if (token == JsDocToken.LC) { skipEOLs(); return parseRecordType(next()); } else if (token == JsDocToken.LP) { skipEOLs(); return parseUnionType(next()); } else if (token == JsDocToken.STRING) { String string = stream.getString(); if ("function".equals(string)) { skipEOLs(); return parseFunctionType(next()); } else if ("null".equals(string) || "undefined".equals(string)) { return newStringNode(string); } else { return parseTypeName(token); } } restoreLookAhead(token); return reportGenericTypeSyntaxWarning(); } /** * TypeName := NameExpression | NameExpression TypeApplication * TypeApplication := '.<' TypeExpressionList '>' * TypeExpressionList := TypeExpression // a white lie */ private Node parseTypeName(JsDocToken token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } String typeName = stream.getString(); int lineno = stream.getLineno(); int charno = stream.getCharno(); while (match(JsDocToken.EOL) && typeName.charAt(typeName.length() - 1) == '.') { skipEOLs(); if (match(JsDocToken.STRING)) { next(); typeName += stream.getString(); } } Node typeNameNode = newStringNode(typeName, lineno, charno); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeNameNode.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeNameNode; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType := * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType */ private Node parseFunctionType(JsDocToken token) { // NOTE(nicks

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>antos): We're not implementing generics at the moment, so // just throw out TypeParameters. if (token != JsDocToken.LP) { restoreLookAhead(token); return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); } Node functionType = newNode(Token.FUNCTION); Node parameters = null; skipEOLs(); if (!match(JsDocToken.RP)) { token = next(); boolean hasParams = true; if (token == JsDocToken.STRING) { String tokenStr = stream.getString(); boolean isThis = "this".equals(tokenStr); boolean isNew = "new".equals(tokenStr); if (isThis || isNew) { if (match(JsDocToken.COLON)) { next(); skipEOLs(); Node contextType = wrapNode( isThis ? Token.THIS : Token.NEW, parseTypeName(next())); if (contextType == null) { return null; } functionType.addChildToFront(contextType); } else { return reportTypeSyntaxWarning("msg.jsdoc.missing.colon"); } if (match(JsDocToken.COMMA)) { next(); skipEOLs(); token = next(); } else { hasParams = false; } } } if (hasParams) { parameters = parseParametersType(token); if (parameters == null) { return null; } } } if (parameters != null) { functionType.addChildToBack(parameters); } skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } skipEOLs(); Node resultType = parseResultType(next()); if (resultType == null) { return null; } else { functionType.addChildToBack(resultType); } return functionType; } /** * ParametersType := RestParameterType | NonRestParametersType * | NonRestParametersType ',' RestParameterType * RestParameterType := '...' Identifier * NonRestParametersType := ParameterType ',' NonRestParametersType * | ParameterType * | OptionalParametersType * OptionalParametersType := OptionalParameterType

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = wrapNode(Token.EQUALS, paramType); } } if (paramType == null) { return null; } paramsType.addChildToBack(paramType); if (isVarArgs) { break; } } while (match(JsDocToken.COMMA)); } if (isVarArgs && match(JsDocToken.COMMA)) { return reportTypeSyntaxWarning("msg.jsdoc.function.varargs"); } // The right paren will be checked by parseFunctionType return paramsType; } /** * ResultType := <empty> | ':' void | ':' TypeExpression */ private Node parseResultType(JsDocToken token) { skipEOLs(); if (!match(JsDocToken.COLON)) { return newNode(Token.EMPTY); } token = next(); skipEOLs(); if (match(JsDocToken.STRING) && "void".equals(stream.getString())) { next(); return newNode(Token.VOID); } else { return parseTypeExpression(next()); } } /** * UnionType := '(' TypeUnionList ')' * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList * * We've removed the empty union type. */ private Node parseUnionType(JsDocToken token) { return parseUnionTypeWithAlternate(token, null); } /** * Create a new union type, with an alternate that has already been * parsed. The alternate may be null. */ private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { Node union = newNode(Token.PIPE); if (alternate != null) { union.addChildToBack(alternate); } Node expr = null; do { if (expr != null) { skipEOLs(); token = next(); Preconditions.checkState( token == JsDocToken.PIPE || token == JsDocToken.COMMA); boolean isPipe = token == JsDocToken.PIPE; if (isPipe && match(JsDocToken.PIPE)) { // We support double pipes for backwards compatibility. next(); } skipEOLs(); token = next(); } expr = parseTypeExpression(token); if (expr == null) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> do { Node fieldType = parseFieldType(token); if (fieldType == null) { return null; } fieldTypeList.addChildToBack(fieldType); skipEOLs(); if (!match(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newStringNode(String s) { return newStringNode(s, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s, int lineno, int charno) { Node n = Node.newString(s, lineno, charno).clonePropsFrom(templateNode); n.setLength

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> public void process(Node externs, Node root) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); // Gather the list of function nodes that have @nosideeffects annotations. // For use by SetNoSideEffectCallProperty. NodeTraversal.traverse( compiler, externs, new GatherNoSideEffectFunctions(true)); NodeTraversal.traverse( compiler, root, new GatherNoSideEffectFunctions(false)); NodeTraversal.traverse(compiler, root, new SetNoSideEffectCallProperty(defFinder)); } /** * Determines if the type of the value of the RHS expression can * be a function node. */ private static boolean definitionTypeContainsFunctionType(Definition def) { Node rhs = def.getRValue(); if (rhs == null) { return true; } switch (rhs.getType()) { case Token.ASSIGN: case Token.AND: case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.FUNCTION: case Token.HOOK: case Token.NAME: case Token.NEW: case Token.OR: return true; default: return false; } } /** * Get the value of the @nosideeffects annotation stored in the * doc info. */ private static boolean hasNoSideEffectsAnnotation(Node node) { JSDocInfo docInfo = node.getJSDocInfo(); return docInfo != null && docInfo.isNoSideEffects(); } /** * Gather function nodes that have @nosideeffects annotations. */ private class GatherNoSideEffectFunctions extends AbstractPostOrderCallback { private final boolean inExterns; GatherNoSideEffectFunctions(boolean inExterns) { this.inExterns = inExterns; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { if (!inExterns && hasNoSideEffectsAnnotation(node)) { traversal.report(node, INVALID_NO_SIDE_EFFECT_ANNOTATION); } if (node.isGetProp()) { if (parent.isExprResult() && hasNoSideEffectsAnnotation(node))

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Compiler compiler) { this.compiler = compiler; this.definitionSiteMap = Maps.newLinkedHashMap(); this.nameDefinitionMultimap = LinkedHashMultimap.create(); this.nameUseSiteMultimap = LinkedHashMultimap.create(); } /** * Returns the collection of definition sites found during traversal. * * @return definition site collection. */ public Collection<DefinitionSite> getDefinitionSites() { return definitionSiteMap.values(); } private DefinitionSite getDefinitionAt(Node node) { return definitionSiteMap.get(node); } DefinitionSite getDefinitionForFunction(Node function) { Preconditions.checkState(function.isFunction()); return getDefinitionAt(getNameNodeFromFunctionNode(function)); } @Override public Collection<Definition> getDefinitionsReferencedAt(Node useSite) { if (definitionSiteMap.containsKey(useSite)) { return null; } if (useSite.isGetProp()) { String propName = useSite.getLastChild().getString(); if (propName.equals("apply") || propName.equals("call")) { useSite = useSite.getFirstChild(); } } String name = getSimplifiedName(useSite); if (name != null) { Collection<Definition> defs = nameDefinitionMultimap.get(name); if (!defs.isEmpty()) { return defs; } else { return null; } } else { return null; } } @Override public void process(Node externs, Node source) { NodeTraversal.traverse( compiler, externs, new DefinitionGatheringCallback(true)); NodeTraversal.traverse( compiler, source, new DefinitionGatheringCallback(false)); NodeTraversal.traverse( compiler, source, new UseSiteGatheringCallback()); } /** * Returns a collection of use sites that may refer to provided * definition. Returns an empty collection if the definition is not * used anywhere. * * @param definition Definition of interest. * @return use site collection. */ Collection<UseSite> getUseSites(Definition definition) { String name = getSimplifiedName(definition.getLValue()); return nameUseSiteMultimap.get(name); } /** * Extract a name from a node

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>. In the case of GETPROP nodes, * replace the namespace or object expression with "this" for * simplicity and correctness at the expense of inefficiencies due * to higher chances of name collisions. * * TODO(user) revisit. it would be helpful to at least use fully * qualified names in the case of namespaces. Might not matter as * much if this pass runs after "collapsing properties". */ private static String getSimplifiedName(Node node) { if (node.isName()) { String name = node.getString(); if (name != null && !name.isEmpty()) { return name; } else { return null; } } else if (node.isGetProp()) { return "this." + node.getLastChild().getString(); } return null; } private class DefinitionGatheringCallback extends AbstractPostOrderCallback { private boolean inExterns; DefinitionGatheringCallback(boolean inExterns) { this.inExterns = inExterns; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { // Arguments of external functions should not count as name // definitions. They are placeholder names for documentation // purposes only which are not reachable from anywhere. if (inExterns && node.isName() && parent.isParamList()) { return; } Definition def = DefinitionsRemover.getDefinition(node, inExterns); if (def != null) { String name = getSimplifiedName(def.getLValue()); if (name != null) { Node rValue = def.getRValue(); if ((rValue != null) && !NodeUtil.isImmutableValue(rValue) && !rValue.isFunction()) { // Unhandled complex expression Definition unknownDef = new UnknownDefinition(def.getLValue(), inExterns); def = unknownDef; } // TODO(johnlenz) : remove this stub dropping code if it becomes // illegal to have untyped stubs in the externs definitions. if (inExterns) { // We need special handling of untyped externs stubs here: // the stub should be dropped if the name is

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>ches the case where an object literal in goog.reflect.object // and a prototype method have the same property name. // NOTE(nicksantos): Maps and trogedit both do this by different // mechanisms. Node nameNode = site.node; Collection<Definition> singleSiteDefinitions = getDefinitionsReferencedAt(nameNode); if (singleSiteDefinitions.size() > 1) { return false; } Preconditions.checkState(!singleSiteDefinitions.isEmpty()); Preconditions.checkState(singleSiteDefinitions.contains(definition)); } return true; } /** * @return Whether the definition is directly exported. */ private boolean isExported(Definition definition) { // Assume an exported method result is used. Node lValue = definition.getLValue(); if (lValue == null) { return true; } String partialName; if (lValue.isGetProp()) { partialName = lValue.getLastChild().getString(); } else if (lValue.isName()) { partialName = lValue.getString(); } else { // GETELEM is assumed to be an export or other expression are unknown // uses. return true; } CodingConvention codingConvention = compiler.getCodingConvention(); if (codingConvention.isExported(partialName)) { return true; } return false; } /** * @return Whether the function is defined in a non-aliasing expression. */ static boolean isSimpleFunctionDeclaration(Node fn) { Node parent = fn.getParent(); Node gramps = parent.getParent(); // Simple definition finder doesn't provide useful results in some // cases, specifically: // - functions with recursive definitions // - functions defined in object literals // - functions defined in array literals // Here we defined a set of known function declaration that are 'ok'. // Some projects seem to actually define "JSCompiler_renameProperty" // rather than simply having an extern definition. Don't mess with it. Node nameNode = SimpleDefinitionFinder.getNameNodeFromFunctionNode(fn); if (nameNode != null && nameNode.isName()) { String name = nameNode.getString(); if (name.equals(NodeUtil.JSC_PROPERTY_NAME_FN

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) || name.equals( ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING)) { return false; } } // example: function a(){}; if (NodeUtil.isFunctionDeclaration(fn)) { return true; } // example: a = function(){}; // example: var a = function(){}; if (fn.getFirstChild().getString().isEmpty() && (NodeUtil.isExprAssign(gramps) || parent.isName())) { return true; } return false; } /** * @return the node defining the name for this function (if any). */ static Node getNameNodeFromFunctionNode(Node function) { Preconditions.checkState(function.isFunction()); if (NodeUtil.isFunctionDeclaration(function)) { return function.getFirstChild(); } else { Node parent = function.getParent(); if (NodeUtil.isVarDeclaration(parent)) { return parent; } else if (parent.isAssign()) { return parent.getFirstChild(); } else if (NodeUtil.isObjectLitKey(parent, parent.getParent())) { return parent; } } return null; } /** * Traverse a node and its children and remove any references to from * the structures. */ void removeReferences(Node node) { if (DefinitionsRemover.isDefinitionNode(node)) { DefinitionSite defSite = definitionSiteMap.get(node); if (defSite != null) { Definition def = defSite.definition; String name = getSimplifiedName(def.getLValue()); if (name != null) { this.definitionSiteMap.remove(node); this.nameDefinitionMultimap.remove(name, node); } } } else { Node useSite = node; if (useSite.isGetProp()) { String propName = useSite.getLastChild().getString(); if (propName.equals("apply") || propName.equals("call")) { useSite = useSite.getFirstChild(); } } String name = getSimplifiedName(useSite); if (name != null) { this.nameUseSiteMultimap.remove(name, new UseSite(useSite, null, null)); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; /** * Computes reaching definition for all use of each variables. * * A definition of {@code A} in {@code A = foo()} is a reaching definition of * the use of {@code A} in {@code alert(A)} if all paths from entry node must * reaches that definition and it is the last definition before the use. * */ final class MustBeReachingVariableDef extends DataFlowAnalysis<Node, MustBeReachingVariableDef.MustDef> { // The scope of the function that we are analyzing. private final Scope jsScope; private final AbstractCompiler compiler; private final Set<Var> escaped; MustBeReachingVariableDef( ControlFlowGraph<Node>

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> cfg, Scope jsScope, AbstractCompiler compiler) { super(cfg, new MustDefJoin()); this.jsScope = jsScope; this.compiler = compiler; this.escaped = Sets.newHashSet(); computeEscaped(jsScope, escaped, compiler); } /** * Abstraction of a local variable definition. It represents the node which * a local variable is defined as well as a set of other local variables that * this definition reads from. For example N: a = b + foo.bar(c). The * definition node will be N, the depending set would be {b,c}. */ static class Definition { final Node node; final Set<Var> depends = Sets.newHashSet(); private boolean unknownDependencies = false; Definition(Node node) { this.node = node; } @Override public boolean equals(Object other) { if (!(other instanceof Definition)) { return false; } Definition otherDef = (Definition) other; // If the var has the same definition node we can assume they have the // same depends set. return otherDef.node == node; } } /** * Must reaching definition lattice representation. It captures a product * lattice for each local (non-escaped) variable. The sub-lattice is * a n + 2 element lattice with all the {@link Definition} in the program, * TOP and BOTTOM. * * <p>Since this is a Must-Define analysis, BOTTOM represents the case where * there might be more than one reaching definition for the variable. * * * (TOP) * / | | \ * N1 N2 N3 ....Nn * \ | | / * (BOTTOM) * */ static final class MustDef implements LatticeElement { // TODO(user): Use bit vector instead of maps might get better // performance. Change it after this is tested to be fully functional. // When a Var "A" = "TOP", "A" does not exist in reachingDef's keySet. // When a Var "A" = Node N, "A" maps to that node. // When a Var "A" = "BOTTOM", "A" maps to null

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>. final Map<Var, Definition> reachingDef; public MustDef() { reachingDef = Maps.newHashMap(); } public MustDef(Iterator<Var> vars) { this(); while(vars.hasNext()) { Var var = vars.next(); // Every variable in the scope is defined once in the beginning of the // function: all the declared variables are undefined, all functions // have been assigned and all arguments has its value from the caller. reachingDef.put(var, new Definition(var.scope.getRootNode())); } } /** * Copy constructor. * * @param other The constructed object is a replicated copy of this element. */ public MustDef(MustDef other) { reachingDef = Maps.newHashMap(other.reachingDef); } @Override public boolean equals(Object other) { return (other instanceof MustDef) && ((MustDef) other).reachingDef.equals(this.reachingDef); } } private static class MustDefJoin extends JoinOp.BinaryJoinOp<MustDef> { @Override public MustDef apply(MustDef a, MustDef b) { MustDef result = new MustDef(); Map<Var, Definition> resultMap = result.reachingDef; // Take the join of all variables that are not TOP in this. for (Map.Entry<Var, Definition> varEntry : a.reachingDef.entrySet()) { Var var = varEntry.getKey(); Definition aDef = varEntry.getValue(); if (aDef == null) { // "a" is BOTTOM implies that the variable has more than one possible // definition. We set the join of this to be BOTTOM regardless of what // "b" might be. resultMap.put(var, null); continue; } Node aNode = aDef.node; if (b.reachingDef.containsKey(var)) { Definition bDef = b.reachingDef.get(var); if (aDef.equals(bDef)) { resultMap.put(var, aDef); } else { resultMap.put(var, null); } } else { resultMap.put(var, aDef); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // Take the join of all variables that are not TOP in other but it is TOP // in this. for (Map.Entry<Var, Definition> entry : b.reachingDef.entrySet()) { Var var = entry.getKey(); if (!a.reachingDef.containsKey(var)) { resultMap.put(var, entry.getValue()); } } return result; } } @Override boolean isForward() { return true; } @Override MustDef createEntryLattice() { return new MustDef(jsScope.getVars()); } @Override MustDef createInitialEstimateLattice() { return new MustDef(); } @Override MustDef flowThrough(Node n, MustDef input) { // TODO(user): We are doing a straight copy from input to output. There // might be some opportunities to cut down overhead. MustDef output = new MustDef(input); // TODO(user): This must know about ON_EX edges but it should handle // it better than what we did in liveness. Because we are in a forward mode, // we can used the branched forward analysis. computeMustDef(n, n, output, false); return output; } /** * @param n The node in question. * @param cfgNode The node to add * @param conditional true if the definition is not always executed. */ private void computeMustDef( Node n, Node cfgNode, MustDef output, boolean conditional) { switch (n.getType()) { case Token.BLOCK: case Token.FUNCTION: return; case Token.WHILE: case Token.DO: case Token.IF: computeMustDef( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeMustDef( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (lhs.isVar()) { lhs = lhs.getLastChild(); // for(var x in y) {...} }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> if (lhs.isName()) { addToDefIfLocal(lhs.getString(), cfgNode, rhs, output); } } return; case Token.AND: case Token.OR: computeMustDef(n.getFirstChild(), cfgNode, output, conditional); computeMustDef(n.getLastChild(), cfgNode, output, true); return; case Token.HOOK: computeMustDef(n.getFirstChild(), cfgNode, output, conditional); computeMustDef(n.getFirstChild().getNext(), cfgNode, output, true); computeMustDef(n.getLastChild(), cfgNode, output, true); return; case Token.VAR: for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.hasChildren()) { computeMustDef(c.getFirstChild(), cfgNode, output, conditional); addToDefIfLocal(c.getString(), conditional ? null : cfgNode, c.getFirstChild(), output); } } return; default: if (NodeUtil.isAssignmentOp(n)) { if (n.getFirstChild().isName()) { Node name = n.getFirstChild(); computeMustDef(name.getNext(), cfgNode, output, conditional); addToDefIfLocal(name.getString(), conditional ? null : cfgNode, n.getLastChild(), output); return; } else if (NodeUtil.isGet(n.getFirstChild())) { // Treat all assignments to arguments as redefining the // parameters itself. Node obj = n.getFirstChild().getFirstChild(); if (obj.isName() && "arguments".equals(obj.getString())) { // TODO(user): More accuracy can be introduced // i.e. We know exactly what arguments[x] is if x is a constant // number. escapeParameters(output); } } } if (n.isName() && "arguments".equals(n.getString())) { escapeParameters(output); } // DEC and INC actually defines the variable. if (n.isDec() || n.isInc()) { Node target = n.getFirstChild(); if (target.isName()) { addToDefIfLocal(target.getString(), conditional ? null :

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> cfgNode, null, output); return; } } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { computeMustDef(c, cfgNode, output, conditional); } } } /** * Set the variable lattice for the given name to the node value in the def * lattice. Do nothing if the variable name is one of the escaped variable. * * @param node The CFG node where the definition should be record to. * {@code null} if this is a conditional define. */ private void addToDefIfLocal( String name, @Nullable Node node, @Nullable Node rValue, MustDef def) { Var var = jsScope.getVar(name); // var might be null because the variable might be defined in the extern // that we might not traverse. if (var == null || var.scope != jsScope) { return; } for (Var other : def.reachingDef.keySet()) { Definition otherDef = def.reachingDef.get(other); if (otherDef == null) { continue; } if (otherDef.depends.contains(var)) { def.reachingDef.put(other, null); } } if (!escaped.contains(var)) { if (node == null) { def.reachingDef.put(var, null); } else { Definition definition = new Definition(node); if (rValue != null) { computeDependence(definition, rValue); } def.reachingDef.put(var, definition); } } } private void escapeParameters(MustDef output) { for (Iterator<Var> i = jsScope.getVars(); i.hasNext();) { Var v = i.next(); if (isParameter(v)) { // Assume we no longer know where the parameter comes from // anymore. output.reachingDef.put(v, null); } } // Also, assume we no longer know anything that depends on a parameter. for (Entry<Var, Definition> pair: output.reachingDef.entrySet()) { Definition value = pair.getValue(); if (value == null) { continue; } for (Var

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> dep : value.depends) { if (isParameter(dep)) { output.reachingDef.put(pair.getKey(), null); } } } } private boolean isParameter(Var v) { return v.getParentNode().isParamList(); } /** * Computes all the local variables that rValue reads from and store that * in the def's depends set. */ private void computeDependence(final Definition def, Node rValue) { NodeTraversal.traverse(compiler, rValue, new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var dep = jsScope.getVar(n.getString()); if (dep == null) { def.unknownDependencies = true; } else { def.depends.add(dep); } } } }); } /** * Gets the must reaching definition of a given node. * * @param name name of the variable. It can only be names of local variable * that are not function parameters, escaped variables or variables * declared in catch. * @param useNode the location of the use where the definition reaches. */ Definition getDef(String name, Node useNode) { Preconditions.checkArgument(getCfg().hasNode(useNode)); GraphNode<Node, Branch> n = getCfg().getNode(useNode); FlowState<MustDef> state = n.getAnnotation(); return state.getIn().reachingDef.get(jsScope.getVar(name)); } Node getDefNode(String name, Node useNode) { Definition def = getDef(name, useNode); return def == null ? null : def.node; } boolean dependsOnOuterScopeVars(Definition def) { if (def.unknownDependencies) { return true; } for (Var s : def.depends) { if (s.scope != jsScope) { return true; } } return false; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>; } /** * Calculates the preciser scope starting with the first link. */ protected FlowScope firstPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return firstLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } /** * Delegates the calculation of the preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ protected JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(node, qualifiedName, origType, type); break; case Token.THIS: // "this" references aren't currently modeled in the CFG. break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType) */ private final Visitor<JSType> restrictUndefinedVisitor = new Visitor<JSType>() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type)

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> based on types and casting * unnecessary. * */ abstract class TypeSafeDispatcher<T> { abstract T processArrayLiteral(ArrayLiteral literalNode); abstract T processAssignment(Assignment assignmentNode); abstract T processAstRoot(AstRoot rootNode); abstract T processBlock(Block blockNode); abstract T processBreakStatement(BreakStatement statementNode); abstract T processCatchClause(CatchClause clauseNode); abstract T processConditionalExpression(ConditionalExpression exprNode); abstract T processContinueStatement(ContinueStatement statementNode); abstract T processDoLoop(DoLoop loopNode); abstract T processElementGet(ElementGet getNode); abstract T processEmptyExpression(EmptyExpression exprNode); abstract T processEmptyStatement(EmptyStatement exprNode); abstract T processExpressionStatement(ExpressionStatement statementNode); abstract T processForInLoop(ForInLoop loopNode); abstract T processForLoop(ForLoop loopNode); abstract T processFunctionCall(FunctionCall callNode); abstract T processFunctionNode(FunctionNode functionNode); abstract T processIfStatement(IfStatement statementNode); abstract T processInfixExpression(InfixExpression exprNode); abstract T processKeywordLiteral(KeywordLiteral literalNode); abstract T processLabel(Label labelNode); abstract T processLabeledStatement(LabeledStatement statementNode); abstract T processName(Name nameNode); abstract T processNewExpression(NewExpression exprNode); abstract T processNumberLiteral(NumberLiteral literalNode); abstract T processObjectLiteral(ObjectLiteral literalNode); abstract T processObjectProperty(ObjectProperty propertyNode); abstract T processParenthesizedExpression(ParenthesizedExpression exprNode); abstract T processPropertyGet(PropertyGet getNode); abstract T processRegExpLiteral(RegExpLiteral literalNode); abstract T processReturnStatement(ReturnStatement statementNode); abstract T processScope(Scope scopeNode); abstract T processStringLiteral(StringLiteral literalNode); abstract T processSwitchCase(SwitchCase caseNode); abstract T processSwitchStatement(SwitchStatement statementNode); abstract T processThrowStatement(ThrowStatement statementNode); abstract T processTryStatement(TryStatement statementNode); abstract T processUnaryExpression(UnaryExpression exprNode); abstract T processVariableDeclaration(VariableDeclaration declarationNode); abstract T processVariableInitializer(VariableInitializer initializerNode); abstract T processWhileLoop(WhileLoop loopNode); abstract T process

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>WithStatement(WithStatement statementNode); abstract T processIllegalToken(AstNode node); public T process(AstNode node) { switch (node.getType()) { case Token.ADD: case Token.AND: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.OR: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return processInfixExpression((InfixExpression) node); case Token.ARRAYLIT: return processArrayLiteral((ArrayLiteral) node); case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_DIV: case Token.ASSIGN_LSH: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_RSH: case Token.ASSIGN_SUB: case Token.ASSIGN_URSH: return processAssignment((Assignment) node); case Token.BITNOT: case Token.DEC: case Token.DELPROP: case Token.INC: case Token.NEG: case Token.NOT: case Token.POS: case Token.TYPEOF: case Token.VOID: return processUnaryExpression((UnaryExpression) node); case Token.BLOCK: if (node instanceof Block) { return processBlock((Block) node); } else if (node instanceof Scope) { return processScope((Scope) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.BREAK: return processBreakStatement((BreakStatement) node); case Token.CALL: return

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> processFunctionCall((FunctionCall) node); case Token.CASE: case Token.DEFAULT: return processSwitchCase((SwitchCase) node); case Token.CATCH: return processCatchClause((CatchClause) node); case Token.COLON: return processObjectProperty((ObjectProperty) node); case Token.CONTINUE: return processContinueStatement((ContinueStatement) node); case Token.DO: return processDoLoop((DoLoop) node); case Token.EMPTY: return (node instanceof EmptyExpression) ? processEmptyExpression((EmptyExpression) node) : processEmptyStatement((EmptyStatement) node); case Token.EXPR_RESULT: case Token.EXPR_VOID: if (node instanceof ExpressionStatement) { return processExpressionStatement((ExpressionStatement) node); } else if (node instanceof LabeledStatement) { return processLabeledStatement((LabeledStatement) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.DEBUGGER: case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: return processKeywordLiteral((KeywordLiteral) node); case Token.FOR: if (node instanceof ForInLoop) { return processForInLoop((ForInLoop) node); } else if (node instanceof ForLoop) { return processForLoop((ForLoop) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.FUNCTION: return processFunctionNode((FunctionNode) node); case Token.GETELEM: return processElementGet((ElementGet) node); case Token.GETPROP: return processPropertyGet((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name)

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> the <code>Boolean()</code> JavaScript cast function * except it return UNKNOWN for known values with side-effects, use * getImpureBooleanValue if you don't care about side-effects. */ static TernaryValue getPureBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NOT: return getPureBooleanValue(n.getLastChild()).not(); case Token.NULL: case Token.FALSE: return TernaryValue.FALSE; case Token.VOID: if (!mayHaveSideEffects(n.getFirstChild())) { return TernaryValue.FALSE; } break; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.REGEXP: return TernaryValue.TRUE; case Token.ARRAYLIT: case Token.OBJECTLIT: if (!mayHaveSideEffects(n)) { return TernaryValue.TRUE; } break; } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): regex literals as well. switch (n.getType()) { case Token.STRING: case Token.STRING_KEY: return n.getString(); case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name)) { return name; } break; case Token.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Number()</code> JavaScript cast function. */ static Double getNumberValue(Node n) { switch (n.getType()) { case Token.TRUE: return 1.0; case Token.FALSE: case Token.NULL: return 0.0; case Token.NUMBER: return n.getDouble(); case Token.VOID: if (mayHaveSideEffects(n.getFirstChild())) { return null; } else { return Double.NaN; } case Token.NAME: // Check for known constants String name = n.getString(); if (name.equals("undefined")) { return Double.NaN; } if (name.equals("NaN")) { return Double.NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().isName() && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { // Attempt to convert hex numbers.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>TRUE; default: return (Character.getType(c) == Character.SPACE_SEPARATOR) ? TernaryValue.TRUE : TernaryValue.FALSE; } } /** * Gets the function's name. This method recognizes five forms: * <ul> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Preconditions.checkState(n.isFunction()); Node parent = n.getParent(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getQualifiedName(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... String name = n.getFirstChild().getQualifiedName(); return name; } } /** * Gets the function's name. This method recognizes the forms: * <ul> * <li>{@code &#123;'name': function() ...&#125;}</li> * <li>{@code &#123;name: function() ...&#125;}</li> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * * @param n a

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ public static String getNearestFunctionName(Node n) { if (!n.isFunction()) { return null; } String name = getFunctionName(n); if (name != null) { return name; } // Check for the form { 'x' : function() { } } Node parent = n.getParent(); switch (parent.getType()) { case Token.SETTER_DEF: case Token.GETTER_DEF: case Token.STRING_KEY: // Return the name of the literal's key. return parent.getString(); case Token.NUMBER: return getStringValue(parent); } return null; } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: return true; case Token.CAST: case Token.NOT: return isImmutableValue(n.getFirstChild()); case Token.VOID: case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if the operator on this node is symmetric */ static boolean isSymmetricOperation(Node n) { switch (n.getType()) { case Token.EQ: // equal case Token.NE: // not equal case Token.SHEQ: // exactly equal case Token.SHNE: // exactly not equal case Token.MUL: // multiply, unlike add it only works on numbers // or results NaN if any of the operators is not a number return true; } return false; } /** * Returns true if the operator on this node is relational. * the returned set does not include the equalities.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Unary operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false;

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) { return true; } break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return checkForNewObjects || !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperator(n)) { break; } if (isAssignmentOp(n)) { Node assignTarget = n.getFirstChild(); if (assignTarget.isName()) { return true; } // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } if (isGet(assignTarget)) { // If the object being assigned to is a local object, don't // consider this a side-effect as it can't be referenced // elsewhere. Don't do this recursively as the property might // be an alias of another object, unlike a literal below. Node current = assignTarget.getFirstChild(); if (evaluatesToLocalValue(current)) { return false; } // A literal value as defined by "isLiteralValue" is guaranteed // not to be an alias, or any components which are aliases of

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // other objects. // If the root object is a literal don't consider this a // side-effect. while (isGet(current)) { current = current.getFirstChild(); } return !isLiteralValue(current, true); } else { // TODO(johnlenz): remove this code and make this an exception. This // is here only for legacy reasons, the AST is not valid but // preserve existing behavior. return !isLiteralValue(assignTarget, true); } } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - constructor call node */ static boolean constructorCallHasSideEffects(Node callNode) { return constructorCallHasSideEffects(callNode, null); } static boolean constructorCallHasSideEffects( Node callNode, AbstractCompiler compiler) { if (!callNode.isNew()) { throw new IllegalStateException( "Expected NEW node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.isName() && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } // A list of built-in object creation or primitive type cast functions that // can also be called as constructors but lack side-effects. // TODO(johnlenz): consider adding an extern annotation for this. private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS = ImmutableSet.of( "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); private static final Set<String> OBJECT_METHODS_WITHOUT_SIDEEFFECTS = ImmutableSet.of("toString", "valueOf"); private static final Set<String> REGEXP_METHODS = ImmutableSet.of("test", "exec");

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> private static final Set<String> STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects(Node callNode) { return functionCallHasSideEffects(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The call node to inspected. * @param compiler A compiler object to provide program state changing * context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { if (!callNode.isCall()) { throw new IllegalStateException( "Expected CALL node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.isName()) { String name = nameNode.getString(); if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) { return false; } } else if (nameNode.isGetProp()) { if (callNode.hasOneChild() && OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains( nameNode.getLastChild().getString())) { return false; } if (callNode.isOnlyModifiesThisCall() && evaluatesToLocalValue(nameNode.getFirstChild())) { return false; } // Math.floor has no side-effects. // TODO(nicksantos): This is a terrible terrible hack, until // I create a definitionProvider that understands namespacing. if (nameNode.getFirstChild().isName()) { if ("Math.floor".equals(nameNode.getQualifiedName())) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().isRegExp() && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false;

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } else if (nameNode.getFirstChild().isString() && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.isString() || param.isRegExp())) { return false; } } } } return true; } /** * @return Whether the call has a local result. */ static boolean callHasLocalResult(Node n) { Preconditions.checkState(n.isCall()); return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0; } /** * @return Whether the new has a local result. */ static boolean newHasLocalResult(Node n) { Preconditions.checkState(n.isNew()); return n.isOnlyModifiesThisCall(); } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return 6; case Token.BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9; case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.CALL: case Token.GETELEM: case Token.GETPROP: // Data values case Token.ARRAYLIT: case Token.EMPTY: // TODO(johnlenz): remove this. case Token.FALSE: case Token.FUNCTION: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.STRING: case Token.STRING_KEY: case Token.THIS: case Token.TRUE: return 15; case Token.CAST: return 16; default: throw new Error("Unknown precedence for " + Token.name(type

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) + " (type " + type + ")"); } } static boolean isUndefined(Node n) { switch (n.getType()) { case Token.VOID: return true; case Token.NAME: return n.getString().equals("undefined"); } return false; } static boolean isNullOrUndefined(Node n) { return n.isNull() || isUndefined(n); } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean allResultsMatch(Node n, Predicate<Node> p) { switch (n.getType()) { case Token.CAST: return allResultsMatch(n.getFirstChild(), p); case Token.ASSIGN: case Token.COMMA: return allResultsMatch(n.getLastChild(), p); case Token.AND: case Token.OR: return allResultsMatch(n.getFirstChild(), p) && allResultsMatch(n.getLastChild(), p); case Token.HOOK: return allResultsMatch(n.getFirstChild().getNext(), p) && allResultsMatch(n.getLastChild(), p); default: return p.apply(n); } } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean anyResultsMatch(Node n, Predicate<Node> p) { switch (n.getType()) { case Token.CAST: return anyResultsMatch(n.getFirstChild(), p); case Token.ASSIGN: case Token.COMMA: return anyResultsMatch(n.getLastChild(), p); case Token.AND: case Token.OR: return anyResultsMatch(n.getFirstChild(), p) || anyResultsMatch(n.getLastChild(), p); case Token.HOOK: return anyResultsMatch(n.getFirstChild().getNext(), p) || anyResultsMatch(n.getLastChild(), p); default: return p.apply(n); } } static class NumbericResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return isNumericResultHelper(n); } } static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE = new NumbericResultPredicate();

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> /** * Returns true if the result of node evaluation is always a number */ static boolean isNumericResult(Node n) { return allResultsMatch(n, NUMBERIC_RESULT_PREDICATE); } static boolean isNumericResultHelper(Node n) { switch (n.getType()) { case Token.ADD: return !mayBeString(n.getFirstChild()) && !mayBeString(n.getLastChild()); case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MUL: case Token.MOD: case Token.DIV: case Token.INC: case Token.DEC: case Token.POS: case Token.NEG: case Token.NUMBER: return true; case Token.NAME: String name = n.getString(); if (name.equals("NaN")) { return true; } if (name.equals("Infinity")) { return true; } return false; default: return false; } } static class BooleanResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return isBooleanResultHelper(n); } } static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE = new BooleanResultPredicate(); /** * @return Whether the result of node evaluation is always a boolean */ static boolean isBooleanResult(Node n) { return allResultsMatch(n, BOOLEAN_RESULT_PREDICATE); } static boolean isBooleanResultHelper(Node n) { switch (n.getType()) { // Primitives case Token.TRUE: case Token.FALSE: // Comparisons case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: // Queries case Token.IN: case Token.INSTANCEOF: // Inversion case Token.NOT: // delete operator returns a boolean. case Token.DELPROP: return true; default: return

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>CASE: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.isScript() || n.isBlock(); } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { return isStatementParent(n.getParent()); } static boolean isStatementParent(Node parent) { // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.isCase() || n.isDefaultCase(); } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return n.isName() && !n.getString().isEmpty();

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.isTry() && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Whether the node is a CATCH container BLOCK. */ static boolean isTryCatchNodeContainer(Node n) { Node parent = n.getParent(); return parent.isTry() && parent.getFirstChild().getNext() == n; } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { if (isTryFinallyNode(parent, node)) { if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) { // A finally can only be removed if there is a catch. parent.removeChild(node); } else { // Otherwise, only its children can be removed. node.detachChildren(); } } else if (node.isCatch()) { // The CATCH can can only be removed if there is a finally clause. Node tryNode = node.getParent().getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachFromParent(); } else if (isTryCatchNodeContainer(node)) { // The container node itself can't be removed, but the contained CATCH // can if there is a 'finally' clause Node tryNode = node.getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachChildren(); } else if (node.isBlock()) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (isStatementBlock(parent) || isSwitchCase(node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.isVar()) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.isFunction() && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().isScript() || n.getParent().getParent().isFunction()); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to the current scope. * * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> expressions: * <pre> * function f() {} * if (x); else function f() {} * for (;;) { function f() {} } * </pre> * * @param n A node * @return Whether n is an function used within an expression. */ static boolean isFunctionExpression(Node n) { return n.isFunction() && !isStatement(n); } /** * Returns whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope). */ static boolean isBleedingFunctionName(Node n) { return n.isName() && !n.getString().isEmpty() && isFunctionExpression(n.getParent()); } /** * Determines if a node is a function expression that has an empty body. * * @param node a node * @return whether the given node is a function expression that is empty */ static boolean isEmptyFunctionExpression(Node node) { return isFunctionExpression(node) && isEmptyBlock(node.getLastChild

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>()); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { // TODO(johnlenz): rename this function Preconditions.checkArgument(function.isFunction()); return isNameReferenced( function.getLastChild(), "arguments", MATCH_NOT_FUNCTION); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.isCall()) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.isString()) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * Determines whether this node is strictly on the left hand side of an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isVarOrSimpleAssignLhs(Node n, Node parent) { return (parent.isAssign() && parent.getFirstChild() == n) || parent.isVar(); } /** * Determines whether this node is used as an L-value. Notice that sometimes * names are

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> used as both L-values and R-values. * * We treat "var x;" as a pseudo-L-value, which kind of makes sense if you * treat it as "assignment to 'undefined' at the top of the scope". But if * we're honest with ourselves, it doesn't make sense, and we only do this * because it makes sense to treat this as syntactically similar to * "var x = 0;". * * @param n The node * @return True if n is an L-value. */ public static boolean isLValue(Node n) { Preconditions.checkArgument(n.isName() || n.isGetProp() || n.isGetElem()); Node parent = n.getParent(); if (parent == null) { return false; } return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || (NodeUtil.isForIn(parent) && parent.getFirstChild() == n) || parent.isVar() || (parent.isFunction() && parent.getFirstChild() == n) || parent.isDec() || parent.isInc() || parent.isParamList() || parent.isCatch(); } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { switch (node.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.isBlock() || addingRoot.isScript()); Preconditions.checkState(addingRoot.getFirstChild() == null || !addingRoot.getFirstChild().isScript()); return addingRoot; } /** * Creates a node representing a qualified name. * * @param name A qualified

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>HashMap(); @Override public void visit(Node n) { if (n.isName()) { Node parent = n.getParent(); if (parent != null && parent.isVar()) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, MATCH_NOT_FUNCTION); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } /** * @return Whether the node represents a qualified prototype property. */ static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (cur.isGetProp()) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> srcReferenceNode) { Node node = IR.voidNode(IR.number(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = IR.name(name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.srcref(value); } Node var = IR.var(nodeName).srcref(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } @Override public boolean apply(Node n) { return n.isName() && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } @Override public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { @Override public boolean apply(Node n) { return isFunctionDeclaration(n) || n.isVar(); } } /** * A predicate for matching anything except function nodes. */ private static class MatchNotFunction implements Predicate<Node>{ @Override public boolean apply(Node n) { return !n.isFunction(); } } static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction(); /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ @Override public boolean apply(Node n) { Node parent = n.getParent(); return n.isBlock() || (!n.isFunction() && (parent == null || isControlStructure(parent

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } /** * Interface for use with the visit method. * @see #visit */ static interface Visitor { void visit(Node node); } /** * A pre-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor visitor, Predicate<Node> traverseChildrenPred) { visitor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, visitor, traverseChildrenPred); } } } /** * A post-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor visitor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, visitor, traverseChildrenPred); } } visitor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.isTry()); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.isTry()); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.isBlock()); return n.hasChildren() && n.getFirstChild().isCatch(); } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ public static Node getFunctionParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name including $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { String name = node.getString(); if (parent.isGetProp() && node == parent.getLastChild()) { return convention.isConstantKey(name); } else if (isObjectLitKey(node, parent)) { return convention.isConstantKey(name); } else { return convention.isConstant(name); } } /** * Get the JSDocInfo for a function. */ public static JSDocInfo getFunctionJSDocInfo(Node n) { Preconditions.checkState(n.isFunction()); JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.isAssign()) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.isName()) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its an

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluatesToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.INC: case Token.DEC: if (value.getBooleanProp(Node.INCRDECR_PROP)) { return evaluatesToLocalValue(value.getFirstChild(), locals); } else { return true; } case Token.THIS: return locals.apply(value); case Token.NAME: return isImmutableValue(value) || locals.apply(value); case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return locals.apply(value); case Token.CALL: return callHasLocalResult(value) || isToStringMethodCall(value) || locals.apply(value); case Token.NEW: return newHasLocalResult(value) || locals.apply(value); case Token.FUNCTION: case Token.REGEXP: case Token.ARRAYLIT: case Token.OBJECTLIT: // Literals objects with non-literal children are allowed. return true; case Token.DELPROP: case Token.IN: // TODO(johnlenz): should IN operator be included in #isSimpleOperator? return true; default: // Other op force a local value: // x = '' + g (x is now an local string) // x -= g (x is now an local number) if (isAssignmentOp(value) || isSimpleOperator(value) || isImmutableValue(value)) { return true; } throw new IllegalStateException( "Unexpected expression node" + value + "\n parent:" + value.getParent()); }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } /** * Given the first sibling, this returns the nth * sibling or null if no such sibling exists. * This is like "getChildAtIndex" but returns null for non-existent indexes. */ private static Node getNthSibling(Node first, int index) { Node sibling = first; while (index != 0 && sibling != null) { sibling = sibling.getNext(); index--; } return sibling; } /** * Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } /** * Returns whether this is a target of a call or new. */ static boolean isCallOrNewTarget(Node target) { Node parent = target.getParent(); return parent != null && NodeUtil.isCallOrNew(parent) && parent.getFirstChild() == target; } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return propNode.isString() && "toString".equals(propNode.getString()); } return false; } /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { Node parent = n.getParent(); if (parent == null) { return null; } if (parent.isName()) { return getBestJSDocInfo(parent); } else if (parent.isAssign()) { return parent.getJSDocInfo(); } else if (isObjectLitKey(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> (isObjectLitKey(lValue, lValue.getParent())) { return getBestLValue(lValue.getParent()); } else if (isGet(lValue)) { return lValue.getFirstChild(); } return null; } /** Get the name of the given l-value node. */ static String getBestLValueName(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue, lValue.getParent())) { Node owner = getBestLValue(lValue.getParent()); if (owner != null) { String ownerName = getBestLValueName(owner); if (ownerName != null) { return ownerName + "." + getObjectLitKeyName(lValue); } } return null; } return lValue.getQualifiedName(); } /** * @returns false iff the result of the expression is not consumed. */ static boolean isExpressionResultUsed(Node expr) { // TODO(johnlenz): consider sharing some code with trySimpleUnusedResult. Node parent = expr.getParent(); switch (parent.getType()) { case Token.BLOCK: case Token.EXPR_RESULT: return false; case Token.CAST: return isExpressionResultUsed(parent); case Token.HOOK: case Token.AND: case Token.OR: return (expr == parent.getFirstChild()) ? true : isExpressionResultUsed(parent); case Token.COMMA: Node gramps = parent.getParent(); if (gramps.isCall() && parent == gramps.getFirstChild()) { // Semantically, a direct call to eval is different from an indirect // call to an eval. See ECMA-262 S15.1.2.1. So it's OK for the first // expression to a comma to be a no-op if it's used to indirect // an eval. This we pretend that this is "used". if (expr == parent.getFirstChild() && parent.getChildCount() == 2 && expr.getNext().isName() && "eval".equals(expr.getNext().getString())) { return true; } } return (expr

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>("NaN"); } else if (value == Double.POSITIVE_INFINITY) { result = IR.name("Infinity"); } else if (value == Double.NEGATIVE_INFINITY) { result = IR.neg(IR.name("Infinity")); } else { result = IR.number(value); } if (srcref != null) { result.srcrefTree(srcref); } return result; } static boolean isNaN(Node n) { if ((n.isName() && n.getString().equals("NaN")) || (n.getType() == Token.DIV && n.getFirstChild().isNumber() && n.getFirstChild().getDouble() == 0 && n.getLastChild().isNumber() && n.getLastChild().getDouble() == 0)) { return true; } return false; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.isEmpty()) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.isComma()) { addExpr(first, NodeUtil.precedence(Token.ASSIGN), Context.OTHER); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addArrayList(first); add("]"); break; case Token.PARAM_LIST: add("("); addList(first); add(")"); break; case Token.COMMA: Preconditions.checkState(childCount == 2); unrollBinaryOperator(n, Token.COMMA, ",", context, Context.OTHER, 0, 0); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type), Context.OTHER); break; } case Token.NEG: { Preconditions.checkState(childCount == 1); // It's important to our sanity checker that the code // we print produces the same AST as the code we parse back. // NEG is a weird case because Rhino parses "- -2" as "2". if (n.getFirstChild().isNumber()) { cc.addNumber(-n.getFirstChild().getDouble()); } else { cc.addOp(NodeUtil.opToStrNoFail(type), false);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> addExpr(first, NodeUtil.precedence(type), Context.OTHER); } break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), 1, Context.OTHER); cc.addOp(":", true); addExpr(last, 1, Context.OTHER); break; } case Token.REGEXP: if (!first.isString() || !last.isString()) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.FUNCTION: if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.GETTER_DEF: case Token.SETTER_DEF: Preconditions.checkState(n.getParent().isObjectLit()); Preconditions.checkState(childCount == 1); Preconditions.checkState(first.isFunction()); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GETTER_DEF) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. if (!n.isQuotedString() && TokenStream.isJSIdentifier(name) && // do not encode literally any non-literal characters that were // Unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. double d = getSimpleNumber(name); if (!Double.isNaN(d)) { cc.addNumber(d); } else { addJsString(n); } } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); } boolean preferLineBreaks = type == Token.SCRIPT || (type == Token.BLOCK && !preserveBlock && n.getParent() != null && n.getParent().isScript()); for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.isVar()) { cc.endStatement(); } if (c.isFunction()) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top-level statements are more homogeneous. if (preferLineBreaks) { cc.notePreferredLineBreak(); } } if (preserveBlock) { cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.isVar()) { add(first, Context.IN_FOR_INIT_CLAUSE

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first); add("in"); add(first.getNext()); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } break; case Token.DO: Preconditions.checkState(childCount == 2); add("do"); addNonEmptyStatement(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState( childCount == 2, "Bad GETPROP: expected 2 children, but got %s", childCount); Preconditions.checkState( last.isString(), "Bad GETPROP: RHS should be STRING"); boolean needsParens = (first.isNumber()); if (needsParens) { add("("); } addExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState( childCount == 2, "Bad GETELEM: expected 2 children but got %s", childCount); addExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> functions). if (NodeUtil.containsType( first, Token.CALL, NodeUtil.MATCH_NOT_FUNCTION)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence, Context.OTHER); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING_KEY: Preconditions.checkState( childCount == 1, "Object lit key must have 1 child"); addJsString(n); break; case Token.STRING: Preconditions.checkState( childCount == 0, "A string may not have children"); addJsString(n); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.isGetterDef() || c.isSetterDef()) { add(c); } else { Preconditions.checkState(c.isStringKey()); String key = c.getString(); // Object literal property names don't have to be quoted if they // are not JavaScript keywords if (!c.isQuotedString() && !TokenStream.isKeyword(key) && TokenStream.isJSIdentifier(key) && // do not encode literally any non-literal characters that // were Unicode escaped. NodeUtil.isLatin(key)) { add(key); } else { // Determine if the string is a simple number. double d = getSimpleNumber(key); if (!Double.isNaN(d)) { cc.addNumber(d); } else { addExpr(c, 1, Context.OTHER); } } add(":"); addExpr(c.getFirstChild(), 1, Context

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c < '0' || c > '9') { return false; } } return len > 0 && s.charAt(0) != '0'; } static double getSimpleNumber(String s) { if (isSimpleNumber(s)) { try { long l = Long.parseLong(s); if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) { return l; } } catch (NumberFormatException e) { // The number was too long to parse. Fall through to NaN. } } return Double.NaN; } /** * @return Whether the name is an indirect eval. */ private boolean isIndirectEval(Node n) { return n.isName() && "eval".equals(n.getString()) && !n.getBooleanProp(Node.DIRECT_EVAL); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be printed. */ private void addNonEmptyStatement( Node n, Context context, boolean allowNonBlockChild) { Node nodeToProcess = n; if (!allowNonBlockChild && !n.isBlock()) { throw new Error("Missing BLOCK child."); } // Strip unneeded blocks, that is blocks with <2 children unless // the CodePrinter specifically wants to keep them. if (n.isBlock()) { int count = getNonEmptyChildCount(n, 2); if (count == 0) { if (cc.shouldPreserveExtraBlocks()) { cc.beginBlock(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } else { cc.endStatement(true); } return; } if (count == 1) { // Hack around a couple of browser bugs: // Safari needs a block around function declarations. // IE6/7 needs a block around DO

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>("); add(n, Context.OTHER); add(")"); } else { add(n, context); } } void addList(Node firstInList) { addList(firstInList, true, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument) { addList(firstInList, isArrayOrFunctionArgument, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument, Context lhsContext) { for (Node n = firstInList; n != null; n = n.getNext()) { boolean isFirst = n == firstInList; if (isFirst) { addExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext); } else { cc.listSeparator(); addExpr(n, isArrayOrFunctionArgument ? 1 : 0, Context.OTHER); } } } /** * This function adds a comma-separated list as is specified by an ARRAYLIT * node with the associated skipIndexes array. This is a space optimization * since we avoid creating a whole Node object for each empty array literal * slot. * @param firstInList The first in the node list (chained through the next * property). */ void addArrayList(Node firstInList) { boolean lastWasEmpty = false; for (Node n = firstInList; n != null; n = n.getNext()) { if (n != firstInList) { cc.listSeparator(); } addExpr(n, 1, Context.OTHER); lastWasEmpty = n.isEmpty(); } if (lastWasEmpty) { cc.listSeparator(); } } void addCaseBody(Node caseBody) { cc.beginCaseBody(); add(caseBody); cc.endCaseBody(); } void addAllSiblings(Node n) { for (Node c = n; c != null; c = c.getNext()) { add(c); } } /** Outputs a JS string, using the optimal (single/double) quote character */ private void addJsString(Node n) { String s = n.getString(); boolean useSlashV = n.getBooleanProp

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(Node.SLASH_V); if (useSlashV) { add(jsString(n.getString(), useSlashV)); } else { String cached = ESCAPED_JS_STRINGS.get(s); if (cached == null) { cached = jsString(n.getString(), useSlashV); ESCAPED_JS_STRINGS.put(s, cached); } add(cached); } } private String jsString(String s, boolean useSlashV) { int singleq = 0, doubleq = 0; // could count the quotes and pick the optimal quote character for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case '"': doubleq++; break; case '\'': singleq++; break; } } String doublequote, singlequote; char quote; if (preferSingleQuotes ? (singleq <= doubleq) : (singleq < doubleq)) { // more double quotes so enclose in single quotes. quote = '\''; doublequote = "\""; singlequote = "\\\'"; } else { // more single quotes so escape the doubles quote = '\"'; doublequote = "\\\""; singlequote = "\'"; } return strEscape(s, quote, doublequote, singlequote, "\\\\", outputCharsetEncoder, useSlashV, false); } /** Escapes regular expression */ String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) { return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder, false, true); } /** * Escapes the given string to a double quoted (") JavaScript/JSON string */ String escapeToDoubleQuotedJsString(String s) { return strEscape(s, '"', "\\\"", "\'", "\\\\", null, false, false); } /* If the user doesn't want to specify an output charset encoder, assume they want Latin/ASCII characters only. */ String regexpEscape(String s) { return regexpEscape(s, null); } /** Helper to escape JavaScript string as well as regular expression */ private String strEscape( String s, char quote, String doublequoteEscape,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> maps m1 * and m2 is the map of the union of names with {@link JSType#getLeastSupertype} * to meet the m1 type and m2 type. * * @see NodeTraversal * @see DataFlowAnalysis * */ public class Scope implements StaticScope<JSType>, StaticSymbolTable<Scope.Var, Scope.Var> { private final Map<String, Var> vars = new LinkedHashMap<String, Var>(); private final Scope parent; private final int depth; private final Node rootNode; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; private Var arguments; private static final Predicate<Var> DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES = new Predicate<Var>() { @Override public boolean apply(Var var) { return var.getParentNode() != null && var.getType() == null && // no declared type var.getParentNode().isVar() && !var.isExtern(); } }; /** Stores info about a variable */ public static class Var implements StaticSlot<JSType>, StaticReference<JSType> { /** name */ final String name; /** Var node */ final Node nameNode; /** * The variable's type. */ private JSType type; /** * Whether the variable's type has been inferred or is declared. An inferred * type may change over time (as more code is discovered), whereas a * declared type is a static contract that must be matched. */ private final boolean typeInferred; /** Input source */ final CompilerInput input; /** * The index at which the var is declared. e..g if it's 0, it's the first * declared variable in that scope */ final int index; /** The enclosing scope */ final Scope scope; /** @see #isMarkedEscaped */ private boolean markedEscaped = false; /** @see #isMarkedAssignedExactlyOnce */ private boolean markedAssignedExactlyOnce = false; /** * Creates a variable. * * @param inferred whether its type is inferred (as opposed to declared) */ private Var(boolean inferred, String name, Node nameNode, JSType type, Scope scope,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> int index, CompilerInput input) { this.name = name; this.nameNode = nameNode; this.type = type; this.scope = scope; this.index = index; this.input = input; this.typeInferred = inferred; } /** * Gets the name of the variable. */ @Override public String getName() { return name; } /** * Gets the node for the name of the variable. */ @Override public Node getNode() { return nameNode; } CompilerInput getInput() { return input; } @Override public StaticSourceFile getSourceFile() { return nameNode.getStaticSourceFile(); } @Override public Var getSymbol() { return this; } @Override public Var getDeclaration() { return nameNode == null ? null : this; } /** * Gets the parent of the name node. */ public Node getParentNode() { return nameNode == null ? null : nameNode.getParent(); } /** * Whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope). */ public boolean isBleedingFunction() { return NodeUtil.isFunctionExpression(getParentNode()); } /** * Gets the scope where this variable is declared. */ Scope getScope() { return scope; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return nameNode != null && NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotated by {@code @define}. */ public

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> boolean isDefine() { JSDocInfo info = getJSDocInfo(); return info != null && info.isDefine(); } public Node getInitialValue() { return NodeUtil.getRValueOfLValue(nameNode); } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isTypeInferred()}. */ @Override public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ @Override public JSDocInfo getJSDocInfo() { return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode); } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type = type.resolve(errorReporter, scope); } } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ @Override public boolean isTypeInferred() { return typeInferred; } public String getInputName() { if (input == null) return "<non-file>"; else return input.getName(); } public boolean isNoShadow() { JSDocInfo info = getJSDocInfo(); return info != null && info.isNoShadow(); } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name + "{" + type + "}"; } /** * Record that this is escaped by

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> an inner scope. * * In other words, it's assigned in an inner scope so that it's much harder * to make assertions about its value at a given point. */ void markEscaped() { markedEscaped = true; } /** * Whether this is escaped by an inner scope. * Notice that not all scope creators record this information. */ boolean isMarkedEscaped() { return markedEscaped; } /** * Record that this is assigned exactly once.. * * In other words, it's assigned in an inner scope so that it's much harder * to make assertions about its value at a given point. */ void markAssignedExactlyOnce() { markedAssignedExactlyOnce = true; } /** * Whether this is assigned exactly once. * Notice that not all scope creators record this information. */ boolean isMarkedAssignedExactlyOnce() { return markedAssignedExactlyOnce; } } /** * A special subclass of Var used to distinguish "arguments" in the current * scope. */ // TODO(johnlenz): Include this the list of Vars for the scope. public static class Arguments extends Var { Arguments(Scope scope) { super( false, // no inferred "arguments", // always arguments null, // no declaration node // TODO(johnlenz): provide the type of "Arguments". null, // no type info scope, -1, // no variable index null // input ); } @Override public boolean equals(Object other) { if (!(other instanceof Arguments)) { return false; } Arguments otherVar = (Arguments) other; return otherVar.scope.getRootNode() == scope.getRootNode(); } @Override public int hashCode() { return System.identityHashCode(this); } } /** * Creates a Scope given the parent Scope and the root node of the scope. * @param parent The parent Scope. Cannot be null. * @param rootNode Typically the FUNCTION node. */ Scope(Scope parent, Node rootNode) { Preconditions.checkNotNull(parent); Preconditions.checkArgument(rootNode != parent.rootNode); this.parent = parent; this.rootNode = rootNode; this

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.isBottom = false; this.depth = parent.depth + 1; } /** * Creates a empty Scope (bottom of the lattice). * @param rootNode Typically a FUNCTION node or the global BLOCK node. * @param isBottom Whether this is the bottom of a lattice. Otherwise, * it must be a global scope. */ private Scope(Node rootNode, boolean isBottom) { this.parent = null; this.rootNode = rootNode; this.isBottom = isBottom; this.depth = 0; } static Scope createGlobalScope(Node rootNode) { return new Scope(rootNode, false); } static Scope createLatticeBottom(Node rootNode) { return new Scope(rootNode, true); } /** The depth of the scope. The global scope has depth 0. */ int getDepth() { return depth; } /** Whether this is the bottom of the lattice. */ boolean isBottom() { return isBottom; } /** * Gets the container node of the scope. This is typically the FUNCTION * node or the global BLOCK/SCRIPT node. */ @Override public Node getRootNode() { return rootNode; } public Scope getParent() { return parent; } Scope getGlobalScope() { Scope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticScope<JSType> getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ @Override public JSType getTypeOfThis() { if (isGlobal()) { return ObjectType.cast(rootNode.getJSType()); } Preconditions.checkState(rootNode.isFunction()); JSType nodeType = rootNode.getJSType(); if (nodeType != null && nodeType.isFunctionType()) { return nodeType.toMaybeFunctionType().getTypeOfThis(); } else { return parent.getTypeOfThis(); } } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the variable *

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input); vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } @Override public Var getSlot(String name) { return getVar(name); } @Override public Var getOwnSlot(String name) { return vars.get(name); } /** * Returns the variable, may be null */ public Var getVar(String name) { Var var = vars.get(name); if (var != null) { return var; } else if (parent != null) { // Recurse up the parent Scope return parent.getVar(name); } else { return null; } } /** * Get a unique VAR object to represents "arguments" within this scope */ public Var getArgumentsVar() { if (arguments == null) { arguments = new Arguments(this); } return arguments; } /**

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> * Returns true if a variable is declared. */ public boolean isDeclared(String name, boolean recurse) { Scope scope = this; if (scope.vars.containsKey(name)) return true; if (scope.parent != null && recurse) { return scope.parent.isDeclared(name, recurse); } return false; } /** * Return an iterator over all of the variables declared in this scope. */ public Iterator<Var> getVars() { return vars.values().iterator(); } /** * Return an iterable over all of the variables declared in this scope. */ Iterable<Var> getVarIterable() { return vars.values(); } @Override public Iterable<Var> getReferences(Var var) { return ImmutableList.of(var); } @Override public StaticScope<JSType> getScope(Var var) { return var.scope; } @Override public Iterable<Var> getAllSymbols() { return Collections.unmodifiableCollection(vars.values()); } /** * Returns number of variables in this scope */ public int getVarCount() { return vars.size(); } /** * Returns whether this is the global scope. */ public boolean isGlobal() { return parent == null; } /** * Returns whether this is a local scope (i.e. not the global scope). */ public boolean isLocal() { return !isGlobal(); } /** * Gets all variables declared with "var" but without declared types attached. */ public Iterator<Var> getDeclarativelyUnboundVarsWithoutTypes() { return Iterators.filter( getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES); } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> boolean isAllType() { return true; } @Override public boolean matchesStringContext() { // Be lenient. return true; } @Override public boolean matchesObjectContext() { // Be lenient. return true; } @Override public boolean canBeCalled() { return false; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override String toStringHelper(boolean forAnnotations) { return "*"; } @Override public String getDisplayName() { return "<Any Type>"; } @Override public boolean hasDisplayName() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseAllType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseAllType(that); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall()) { return; } String callName = n.getFirstChild().getQualifiedName(); TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName); if (tweakFunc == null) { return; } if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) { getOverridesCalls.add( new TweakFunctionCall(t.getSourceName(), tweakFunc, n)); return; } // Ensure the first parameter (the tweak ID) is a string literal. Node tweakIdNode = n.getFirstChild().getNext(); if (!tweakIdNode.isString()) { compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR)); return; } String tweakId = tweakIdNode.getString(); // Make sure there is a TweakInfo structure for it. TweakInfo tweakInfo = allTweaks.get(tweakId); if (tweakInfo == null) { tweakInfo = new TweakInfo(tweakId); allTweaks.put(tweakId, tweakInfo); } switch (tweakFunc) { case REGISTER_BOOLEAN: case REGISTER_NUMBER: case REGISTER_STRING: // Ensure the ID contains only valid characters. if (!ID_MATCHER.matchesAllOf(tweakId)) { compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR)); } // Ensure tweaks are registered in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweaks are registered only once. if (tweakInfo.isRegistered()) { compiler.report( t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId)); break; } Node tweakDefaultValueNode = tweakIdNode.getNext().getNext(); tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode); break; case OVERRIDE_DEFAULT_

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; /** * This interface defines how objects capable of creating scopes from the parse * tree behave. * */ interface ScopeCreator { /** * Creates a {@link Scope} object. * * @param n the root node (either a FUNCTION node, a SCRIPT node, or a * synthetic block node whose children are all SCRIPT nodes) * @param parent the parent Scope object (may be null) */ Scope createScope(Node n, Scope parent); }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.annotation.Nullable; /** * A builder for FunctionTypes, because FunctionTypes are so * ridiculously complex. All methods return {@code this} for ease of use. * * Right now, this mostly uses JSDocInfo to infer type information about * functions. In the long term, developers should extend it to use other * signals by overloading the various "inferXXX" methods. For example, we * might want to use {@code goog.inherits} calls as a signal for inheritance, or * {@code return} statements as a signal for return type. * * NOTE(nicksantos): Organizationally, this feels like it should be in Rhino. * But it depends on some coding convention stuff that's really part * of JSCompiler. * * @author nicksantos@google.com (Nick Santos) * @author pascallouis@google.com (Pascal-Louis Perez) */ final class FunctionTypeBuilder { private final String fnName; private final AbstractCompiler compiler; private final CodingConvention codingConvention; private final JSTypeRegistry typeRegistry; private final Node errorRoot; private final String sourceName; private final Scope scope; private FunctionContents contents = UnknownFunctionContents.get(); private JSType returnType = null; private boolean returnTypeInferred = false; private List<ObjectType> implementedInterfaces = null; private List<ObjectType> extendedInterfaces = null; private ObjectType baseType = null; private JSType thisType = null; private boolean isConstructor = false; private boolean makesStructs = false; private boolean makesDicts = false; private boolean isInterface = false; private Node parametersNode = null; private ImmutableList<String> templateTypeNames = ImmutableList.of(); static final DiagnosticType EXTENDS_WITHOUT_TYPEDEF = DiagnosticType.warning( "JSC_EXTENDS_WITHOUT_TYPEDEF", "@extends used without @constructor or @interface for {0}"); static final DiagnosticType EXTENDS_

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString()); return false; } else if (objectType.isEmptyType()) { reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); return false; } else if (objectType.isUnknownType()) { if (hasMoreTagsToResolve(objectType)) { return true; } else { reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); return false; } } else { return true; } } } private class ImplementedTypeValidator implements Predicate<JSType> { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportError(BAD_IMPLEMENTED_TYPE, fnName); return false; } else if (objectType.isEmptyType()) { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); return false; } else if (objectType.isUnknownType()) { if (hasMoreTagsToResolve(objectType)) { return true; } else { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); return false; } } else { return true; } } } /** * @param fnName The function name. * @param compiler The compiler. * @param errorRoot The node to associate with any warning generated by * this builder. * @param sourceName A source name for associating any warnings that * we have to emit. * @param scope The syntactic scope. */ FunctionTypeBuilder(String fnName, AbstractCompiler compiler, Node errorRoot, String sourceName, Scope scope) { Preconditions.checkNotNull(errorRoot); this.fnName = fnName == null ? "" : fnName; this.codingConvention = compiler.getCodingConvention(); this.typeRegistry = compiler.getTypeRegistry(); this.errorRoot = errorRoot; this.sourceName = sourceName; this.compiler = compiler; this.scope = scope; } /** * Sets the contents of this function.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>, codingConvention.isOptionalParameter(currentParam) || oldParamsListHitOptArgs, codingConvention.isVarArgsParameter(currentParam)); } } // Clone any remaining params that aren't in the function literal, // but make them optional. while (oldParams.hasNext()) { paramBuilder.newOptionalParameterFromNode(oldParams.next()); } parametersNode = paramBuilder.build(); } return this; } /** * Infer the return type from JSDocInfo. */ FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info) { if (info != null && info.hasReturnType()) { returnType = info.getReturnType().evaluate(scope, typeRegistry); returnTypeInferred = false; } return this; } /** * Infer the role of the function (whether it's a constructor or interface) * and what it inherits from in JSDocInfo. */ FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { if (info != null) { isConstructor = info.isConstructor(); makesStructs = info.makesStructs(); makesDicts = info.makesDicts(); isInterface = info.isInterface(); if (makesStructs && !isConstructor) { reportWarning(CONSTRUCTOR_REQUIRED, "@struct", fnName); } else if (makesDicts && !isConstructor) { reportWarning(CONSTRUCTOR_REQUIRED, "@dict", fnName); } // base type if (info.hasBaseType()) { if (isConstructor) { JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry); if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) { baseType = (ObjectType) maybeBaseType; } } else { reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName); } } // Implemented interfaces (for constructors only). if (info.getImplementedInterfaceCount() > 0) { if (isConstructor) { implementedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getImplementedInterfaces()) { JSType maybeInterType = t.evaluate(scope, typeRegistry); if (maybeInterType !=

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> null && maybeInterType.setValidator(new ImplementedTypeValidator())) { implementedInterfaces.add((ObjectType) maybeInterType); } } } else if (isInterface) { reportWarning( TypeCheck.CONFLICTING_IMPLEMENTED_TYPE, fnName); } else { reportWarning(CONSTRUCTOR_REQUIRED, "@implements", fnName); } } // extended interfaces (for interfaces only) // We've already emitted a warning if this is not an interface. if (isInterface) { extendedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getExtendedInterfaces()) { JSType maybeInterfaceType = t.evaluate(scope, typeRegistry); if (maybeInterfaceType != null && maybeInterfaceType.setValidator(new ExtendedTypeValidator())) { extendedInterfaces.add((ObjectType) maybeInterfaceType); } } } } return this; } /** * Infers the type of {@code this}. * @param type The type of this if the info is missing. */ FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) { // Look at the @this annotation first. inferThisType(info); if (thisType == null) { ObjectType objType = ObjectType.cast(type); if (objType != null && (info == null || !info.hasType())) { thisType = objType; } } return this; } /** * Infers the type of {@code this}. * @param info The JSDocInfo for this function. */ FunctionTypeBuilder inferThisType(JSDocInfo info) { JSType maybeThisType = null; if (info != null && info.hasThisType()) { // TODO(johnlenz): In ES5 strict mode a function can have a null or // undefined "this" value, but all the existing "@this" annotations // don't declare restricted types. maybeThisType = info.getThisType().evaluate(scope, typeRegistry) .restrictByNotNullOrUndefined(); } if (maybeThisType != null) { thisType = maybeThisType; } return this; } /** * Infer the parameter types from the doc info alone. */

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> FunctionTypeBuilder inferParameterTypes(JSDocInfo info) { // Create a fake args parent. Node lp = IR.paramList(); for (String name : info.getParameterNames()) { lp.addChildToBack(IR.name(name)); } return inferParameterTypes(lp, info); } /** * Infer the parameter types from the list of argument names and * the doc info. */ FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent, @Nullable JSDocInfo info) { if (argsParent == null) { if (info == null) { return this; } else { return inferParameterTypes(info); } } // arguments Node oldParameterType = null; if (parametersNode != null) { oldParameterType = parametersNode.getFirstChild(); } FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry); boolean warnedAboutArgList = false; Set<String> allJsDocParams = (info == null) ? Sets.<String>newHashSet() : Sets.newHashSet(info.getParameterNames()); boolean foundTemplateType = false; boolean isVarArgs = false; for (Node arg : argsParent.children()) { String argumentName = arg.getString(); allJsDocParams.remove(argumentName); // type from JSDocInfo JSType parameterType = null; boolean isOptionalParam = isOptionalParameter(arg, info); isVarArgs = isVarArgsParameter(arg, info); if (info != null && info.hasParameterType(argumentName)) { parameterType = info.getParameterType(argumentName).evaluate(scope, typeRegistry); } else if (oldParameterType != null && oldParameterType.getJSType() != null) { parameterType = oldParameterType.getJSType(); isOptionalParam = oldParameterType.isOptionalArg(); isVarArgs = oldParameterType.isVarArgs(); } else { parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); } warnedAboutArgList |= addParameter( builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs); if (oldParameterType != null) { oldParameterType = oldParameterType.getNext(); } } // Copy over

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> any old parameters that aren't in the param list. if (!isVarArgs) { while (oldParameterType != null && !isVarArgs) { builder.newParameterFromNode(oldParameterType); oldParameterType = oldParameterType.getNext(); } } for (String inexistentName : allJsDocParams) { reportWarning(INEXISTANT_PARAM, inexistentName, fnName); } parametersNode = builder.build(); return this; } /** * @return Whether the given param is an optional param. */ private boolean isOptionalParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isOptionalParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isOptionalArg(); } /** * Determine whether this is a var args parameter. * @return Whether the given param is a var args param. */ private boolean isVarArgsParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isVarArgsParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isVarArgs(); } /** * Infer the template type from the doc info. */ FunctionTypeBuilder inferTemplateTypeName(@Nullable JSDocInfo info) { if (info != null) { templateTypeNames = info.getTemplateTypeNames(); typeRegistry.setTemplateTypeNames(templateTypeNames); } return this; } /** * Add a parameter to the param list. * @param builder A builder. * @param paramType The parameter type. * @param warnedAboutArgList Whether we've already warned about arg ordering * issues (like if optional args appeared before required ones). * @param isOptional Is this an optional parameter? * @param isVarArgs Is this a var args parameter? * @return Whether a warning was emitted. */ private boolean addParameter(FunctionParamBuilder builder, JSType paramType, boolean warnedAboutArgList, boolean isOptional

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>); JSType existingType = typeRegistry.getType(fnName); if (makesStructs) { fnType.setStruct(); } else if (makesDicts) { fnType.setDict(); } if (existingType != null) { boolean isInstanceObject = existingType.isInstanceType(); if (isInstanceObject || fnName.equals("Function")) { FunctionType existingFn = isInstanceObject ? existingType.toObjectType().getConstructor() : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); if (existingFn.getSource() == null) { existingFn.setSource(contents.getSourceNode()); } if (!existingFn.hasEqualCallType(fnType)) { reportWarning(TYPE_REDEFINITION, fnName, fnType.toString(), existingFn.toString()); } return existingFn; } else { // We fall through and return the created type, even though it will fail // to register. We have no choice as we have to return a function. We // issue an error elsewhere though, so the user should fix it. } } maybeSetBaseType(fnType); if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } return fnType; } private void reportWarning(DiagnosticType warning, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, warning, args)); } private void reportError(DiagnosticType error, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, error, args)); } /** * Determines whether the given JsDoc info declares a function type. */ static boolean isFunctionTypeDeclaration(JSDocInfo info) { return info.getParameterCount() > 0 || info.hasReturnType() || info.hasThisType() || info.isConstructor() || info.isInterface(); } /** * The scope that we should declare this function in, if it needs * to be declared in a scope. Notice that TypedScopeCreator takes * care of most scope-declaring. */ private Scope getScopeDeclaredIn() { int dotIndex

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = fnName.indexOf("."); if (dotIndex != -1) { String rootVarName = fnName.substring(0, dotIndex); Var rootVar = scope.getVar(rootVarName); if (rootVar != null) { return rootVar.getScope(); } } return scope; } /** * Check whether a type is resolvable in the future * If this has a supertype that hasn't been resolved yet, then we can assume * this type will be OK once the super type resolves. * @param objectType * @return true if objectType is resolvable in the future */ private static boolean hasMoreTagsToResolve(ObjectType objectType) { Preconditions.checkArgument(objectType.isUnknownType()); if (objectType.getImplicitPrototype() != null) { // constructor extends class if (objectType.getImplicitPrototype().isResolved()) { return false; } else { return true; } } else { // interface extends interfaces FunctionType ctor = objectType.getConstructor(); if (ctor != null) { for (ObjectType interfaceType : ctor.getExtendedInterfaces()) { if (!interfaceType.isResolved()) { return true; } } } return false; } } /** Holds data dynamically inferred about functions. */ static interface FunctionContents { /** Returns the source node of this function. May be null. */ Node getSourceNode(); /** Returns if the function may be in externs. */ boolean mayBeFromExterns(); /** Returns if a return of a real value (not undefined) appears. */ boolean mayHaveNonEmptyReturns(); /** Returns if this consists of a single throw. */ boolean mayHaveSingleThrow(); /** Gets a list of variables in this scope that are escaped. */ Iterable<String> getEscapedVarNames(); /** Gets a list of variables whose properties are escaped. */ Set<String> getEscapedQualifiedNames(); /** Gets the number of times each variable has been assigned. */ Multiset<String> getAssignedNameCounts(); } static class UnknownFunctionContents implements FunctionContents { private static UnknownFunctionContents singleton = new UnknownFunctionContents(); static FunctionContents get() { return singleton; } @Override public

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> /** * Creates a pass to check global name references at the given warning level. */ CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.convention = compiler.getCodingConvention(); this.level = level; } /** * Injects a pre-computed global namespace, so that the same namespace * can be re-used for multiple check passes. Returns this for easy chaining. */ CheckGlobalNames injectNamespace(GlobalNamespace namespace) { Preconditions.checkArgument(namespace.hasExternsRoot()); this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, externs, root); } // Find prototype properties that will affect our analysis. Preconditions.checkState(namespace.hasExternsRoot()); findPrototypeProps("Object", objectPrototypeProps); findPrototypeProps("Function", functionPrototypeProps); objectPrototypeProps.addAll( convention.getIndirectlyDeclaredProperties()); for (Name name : namespace.getNameForest()) { // Skip extern names. Externs are often not runnable as real code, // and will do things like: // var x; // x.method; // which this check forbids. if (name.inExterns) { continue; } checkDescendantNames(name, name.globalSets + name.localSets > 0); } } private void findPrototypeProps(String type, Set<String> props) { Name slot = namespace.getSlot(type); if (slot != null) { for (Ref ref : slot.getRefs()) { if (ref.type == Ref.Type.PROTOTYPE_GET) { Node fullName = ref.getNode().getParent().getParent(); if (fullName.isGetProp()) { props.add(fullName.getLastChild().getString()); } } } } } /** * Checks to make sure all the descendants of a name are defined if they * are referenced. * * @param name A global name. * @param nameIsDefined If true, {@code name} is defined. Otherwise, it's * undefined, and any

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> references to descendant names should emit warnings. */ private void checkDescendantNames(Name name, boolean nameIsDefined) { if (name.props != null) { for (Name prop : name.props) { // if the ancestor of a property is not defined, then we should emit // warnings for all references to the property. boolean propIsDefined = false; if (nameIsDefined) { // if the ancestor of a property is defined, then let's check that // the property is also explicitly defined if it needs to be. propIsDefined = (!propertyMustBeInitializedByFullName(prop) || prop.globalSets + prop.localSets > 0); } validateName(prop, propIsDefined); checkDescendantNames(prop, propIsDefined); } } } private void validateName(Name name, boolean isDefined) { // If the name is not defined, emit warnings for each reference. While // we're looking through each reference, check all the module dependencies. Ref declaration = name.getDeclaration(); Name parent = name.parent; JSModuleGraph moduleGraph = compiler.getModuleGraph(); for (Ref ref : name.getRefs()) { // Don't worry about global exprs. boolean isGlobalExpr = ref.getNode().getParent().isExprResult(); if (!isDefined && !isTypedef(ref)) { if (!isGlobalExpr) { reportRefToUndefinedName(name, ref); } } else if (declaration != null && ref.getModule() != declaration.getModule() && !moduleGraph.dependsOn( ref.getModule(), declaration.getModule())) { reportBadModuleReference(name, ref); } else { // Check for late references. if (ref.scope.isGlobal()) { // Prototype references are special, because in our reference graph, // A.prototype counts as a reference to A. boolean isPrototypeGet = (ref.type == Ref.Type.PROTOTYPE_GET); Name owner = isPrototypeGet ? name : parent; boolean singleGlobalParentDecl = owner != null && owner.getDeclaration() != null && owner.localSets == 0; if (singleGlobalParentDecl && owner.getDeclaration().preOrderIndex > ref.preOrderIndex

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>) { // SubClass.inherits(SuperClass) subclass = callName.getFirstChild(); } else if (callNode.getChildCount() == 3) { // goog.inherits(SubClass, SuperClass) subclass = callName.getNext(); } else { return null; } if (type == SubclassType.MIXIN) { // Only consider mixins that mix two prototypes as related to // inheritance. if (!endsWithPrototype(superclass)) { return null; } if (!isDeprecatedCall) { if (!endsWithPrototype(subclass)) { return null; } // Strip off the prototype from the name. subclass = subclass.getFirstChild(); } superclass = superclass.getFirstChild(); } // bail out if either of the side of the "inherits" // isn't a real class name. This prevents us from // doing something weird in cases like: // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) if (subclass != null && subclass.isUnscopedQualifiedName() && superclass.isUnscopedQualifiedName()) { return new SubclassRelationship(type, subclass, superclass); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName.isGetProp()) { methodName = callName.getLastChild().getString(); } else if (callName.isName()) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> return "superClass_".equals(propertyName) || super.isSuperClassReference(propertyName); } /** * Given a qualified name node, returns whether "prototype" is at the end. * For example: * a.b.c => false * a.b.c.prototype => true */ private boolean endsWithPrototype(Node qualifiedName) { return qualifiedName.isGetProp() && qualifiedName.getLastChild().getString().equals("prototype"); } /** * Extracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Extracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.isGetProp()) { String qualifiedName = callee.getQualifiedName(); if (functionName.equals(qualifiedName)) { Node target = callee.getNext(); if (target != null && target.isString()) { className = target.getString(); } } } } return className; } /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List<String> identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.isArrayLit()) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.isString()) { typeNames.add(name.getString()); } } return typeNames; } } return super.identifyTypeDeclarationCall(n); } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; } @Override public String getSingletonGetterClassName(Node callNode) { Node callArg = callNode.getFirstChild(); String callName = callArg.getQualifiedName(); // Use both the original name and the post-CollapseProperties name. if (!("goog.addSingletonGetter".equals(callName) || "goog$addSingletonGetter".equals(callName)) || callNode.getChildCount() != 2) { return super.getSingletonGetterClassName(callNode); } return callArg.getNext().getQualifiedName(); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { super.applySingletonGetter(functionType, getterType, objectType); functionType.defineDeclaredProperty("getInstance", getterType, functionType.getSource()); functionType.defineDeclaredProperty("instance_", objectType, functionType.getSource()); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.isCall()); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()) || super.isPropertyTestFunction(call); } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Node extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { boolean equivalent = super.isEquivalentTo(node, compareJsType, recurse); if (equivalent) { double thisValue = getDouble(); double thatValue = ((NumberNode) node).getDouble(); if (thisValue == thatValue) { // detect the difference between 0.0 and -0.0. return (thisValue != 0.0) || (1/thisValue == 1/thatValue); } } return false; } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { return (super.isEquivalentTo(

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> value) { putIntProp(propType, value ? 1 : 0); } public void putIntProp(int propType, int value) { removeProp(propType); if (value != 0) { propListHead = createProp(propType, value, propListHead); } } PropListItem createProp(int propType, Object value, PropListItem next) { return new ObjectPropListItem(propType, value, next); } PropListItem createProp(int propType, int value, PropListItem next) { return new IntPropListItem(propType, value, next); } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count--; keys[count] = x.getType(); } Arrays.sort(keys); return keys; } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first == null || first.getType() != Token.NAME) { sb.append("<invalid>"); } else { sb.append(first.getString()); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { default: value = x.toString(); break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb.append

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> type == Token.STRING_KEY) { if (type == Token.STRING_KEY) { int quoted1 = this.getIntProp(QUOTED_PROP); int quoted2 = node.getIntProp(QUOTED_PROP); if (quoted1 != quoted2) { return false; } } int slashV1 = this.getIntProp(SLASH_V); int slashV2 = node.getIntProp(SLASH_V); if (slashV1 != slashV2) { return false; } } else if (type == Token.CALL) { if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) { return false; } } if (recurse) { Node n, n2; for (n = first, n2 = node.first; n != null; n = n.next, n2 = n2.next) { if (!n.isEquivalentTo(n2, compareJsType, true)) { return false; } } } return true; } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { String name = getString(); return name.isEmpty() ? null : name; } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> case Token.NAME: return getString().isEmpty() ? false : true; case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return getString().isEmpty() ? false : true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null;) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.check

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> } public boolean isAnd() { return this.getType() == Token.AND; } public boolean isArrayLit() { return this.getType() == Token.ARRAYLIT; } public boolean isAssign() { return this.getType() == Token.ASSIGN; } public boolean isAssignAdd() { return this.getType() == Token.ASSIGN_ADD; } public boolean isBlock() { return this.getType() == Token.BLOCK; } public boolean isBreak() { return this.getType() == Token.BREAK; } public boolean isCall() { return this.getType() == Token.CALL; } public boolean isCase() { return this.getType() == Token.CASE; } public boolean isCast() { return this.getType() == Token.CAST; } public boolean isCatch() { return this.getType() == Token.CATCH; } public boolean isComma() { return this.getType() == Token.COMMA; } public boolean isContinue() { return this.getType() == Token.CONTINUE; } public boolean isDebugger() { return this.getType() == Token.DEBUGGER; } public boolean isDec() { return this.getType() == Token.DEC; } public boolean isDefaultCase() { return this.getType() == Token.DEFAULT_CASE; } public boolean isDelProp() { return this.getType() == Token.DELPROP; } public boolean isDo() { return this.getType() == Token.DO; } public boolean isEmpty() { return this.getType() == Token.EMPTY; } public boolean isExprResult() { return this.getType() == Token.EXPR_RESULT; } public boolean isFalse() { return this.getType() == Token.FALSE; } public boolean isFor() { return this.getType() == Token.FOR; } public boolean isFunction() { return this.getType() == Token.FUNCTION; } public boolean isGetterDef() { return this.getType() == Token.GETTER_DEF; } public boolean isGetElem() { return this.getType() == Token.GETELEM; } public boolean isGetProp() {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> return this.getType() == Token.GETPROP; } public boolean isHook() { return this.getType() == Token.HOOK; } public boolean isIf() { return this.getType() == Token.IF; } public boolean isIn() { return this.getType() == Token.IN; } public boolean isInc() { return this.getType() == Token.INC; } public boolean isInstanceOf() { return this.getType() == Token.INSTANCEOF; } public boolean isLabel() { return this.getType() == Token.LABEL; } public boolean isLabelName() { return this.getType() == Token.LABEL_NAME; } public boolean isName() { return this.getType() == Token.NAME; } public boolean isNE() { return this.getType() == Token.NE; } public boolean isNew() { return this.getType() == Token.NEW; } public boolean isNot() { return this.getType() == Token.NOT; } public boolean isNull() { return this.getType() == Token.NULL; } public boolean isNumber() { return this.getType() == Token.NUMBER; } public boolean isObjectLit() { return this.getType() == Token.OBJECTLIT; } public boolean isOr() { return this.getType() == Token.OR; } public boolean isParamList() { return this.getType() == Token.PARAM_LIST; } public boolean isRegExp() { return this.getType() == Token.REGEXP; } public boolean isReturn() { return this.getType() == Token.RETURN; } public boolean isScript() { return this.getType() == Token.SCRIPT; } public boolean isSetterDef() { return this.getType() == Token.SETTER_DEF; } public boolean isString() { return this.getType() == Token.STRING; } public boolean isStringKey() { return this.getType() == Token.STRING_KEY; } public boolean isSwitch() { return this.getType() == Token.SWITCH; } public boolean isThis() { return this.getType() == Token.THIS; }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> parent != null && parent.isTry() && NodeUtil.getCatchBlock(parent) == node && !NodeUtil.hasCatchHandler(node)) { return; } // A block transfer control to its first child if it is not empty. Node child = node.getFirstChild(); // Function declarations are skipped since control doesn't go into that // function (unless it is called) while (child != null && child.isFunction()) { child = child.getNext(); } if (child != null) { createEdge(node, Branch.UNCOND, computeFallThrough(child)); } else { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); } // Synthetic blocks if (parent != null) { switch (parent.getType()) { case Token.DEFAULT_CASE: case Token.CASE: case Token.TRY: break; default: if (node.isBlock() && node.isSyntheticBlock()) { createEdge(node, Branch.SYN_BLOCK, computeFollowNode(node, this)); } break; } } } private void handleFunction(Node node) { // A block transfer control to its first child if it is not empty. Preconditions.checkState(node.getChildCount() >= 3); createEdge(node, Branch.UNCOND, computeFallThrough(node.getFirstChild().getNext().getNext())); Preconditions.checkState(exceptionHandler.peek() == node); exceptionHandler.pop(); } private void handleExpr(Node node) { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); connectToPossibleExceptionHandler(node, node); } private void handleThrow(Node node) { connectToPossibleExceptionHandler(node, node); } private void handleTry(Node node) { createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleCatch(Node node) { createEdge(node, Branch.UNCOND, node.getLastChild()); } private void handleBreak(Node node) { String label = null; // See if it is a break with label. if (node.hasChildren()) { label = node.getFirstChild().getString(); }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Node cur; Node previous = null; Node lastJump; Node parent = node.getParent(); /* * Continuously look up the ancestor tree for the BREAK target or the target * with the corresponding label and connect to it. If along the path we * discover a FINALLY, we will connect the BREAK to that FINALLY. From then * on, we will just record the control flow changes in the finallyMap. This * is due to the fact that we need to connect any node that leaves its own * FINALLY block to the outer FINALLY or the BREAK's target but those nodes * are not known yet due to the way we traverse the nodes. */ for (cur = node, lastJump = node; !isBreakTarget(cur, label); cur = parent, parent = parent.getParent()) { if (cur.isTry() && NodeUtil.hasFinally(cur) && cur.getLastChild() != previous) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFallThrough( cur.getLastChild())); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } if (parent == null) { if (compiler.isIdeMode()) { // In IDE mode, we expect that the data flow graph may // not be well-formed. return; } else { throw new IllegalStateException("Cannot find break target."); } } previous = cur; } if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur, this)); } else { finallyMap.put(lastJump, computeFollowNode(cur, this)); } } private void handleContinue(Node node) { String label = null; if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node previous = null; Node lastJump; // Similar to handBreak's logic with a few minor variation. Node parent = node.getParent(); for (cur = node, lastJump = node; !isContinueTarget(cur, parent, label); cur = parent, parent = parent.getParent()) { if

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> for (int type : types) { if (c.getType() == type) { return c; } } } return null; } /** * Checks if target is actually the break target of labeled continue. The * label can be null if it is an unlabeled break. */ public static boolean isBreakTarget(Node target, String label) { return isBreakStructure(target, label != null) && matchLabel(target.getParent(), label); } /** * Checks if target is actually the continue target of labeled continue. The * label can be null if it is an unlabeled continue. */ private static boolean isContinueTarget( Node target, Node parent, String label) { return isContinueStructure(target) && matchLabel(parent, label); } /** * Check if label is actually referencing the target control structure. If * label is null, it always returns true. */ private static boolean matchLabel(Node target, String label) { if (label == null) { return true; } while (target.isLabel()) { if (target.getFirstChild().getString().equals(label)) { return true; } target = target.getParent(); } return false; } /** * Determines if the subtree might throw an exception. */ public static boolean mayThrowException(Node n) { switch (n.getType()) { case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.THROW: case Token.NEW: case Token.ASSIGN: case Token.INC: case Token.DEC: case Token.INSTANCEOF: return true; case Token.FUNCTION: return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) { return true; } } return false; } /** * Determines whether the given node can be terminated with a BREAK node. */ static boolean isBreakStructure(Node n, boolean labeled) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE:

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> register(t, name); } /** * Records a forward-declared type name. We will not emit errors if this * type name never resolves to anything. */ public void forwardDeclareType(String name) { forwardDeclaredTypes.add(name); } /** * Whether this is a forward-declared type name. */ public boolean isForwardDeclaredType(String name) { return forwardDeclaredTypes.contains(name); } /** Determines whether the given JS package exists. */ public boolean hasNamespace(String name) { return namespaces.contains(name); } /** * Looks up a type by name. * * @param jsTypeName The name string. * @return the corresponding JSType object or {@code null} it cannot be found */ public JSType getType(String jsTypeName) { // TODO(user): Push every local type name out of namesToTypes so that // NamedType#resolve is correct. TemplateType templateType = templateTypes.get(jsTypeName); if (templateType != null) { return templateType; } return namesToTypes.get(jsTypeName); } public JSType getNativeType(JSTypeNative typeId) { return nativeTypes[typeId.ordinal()]; } public ObjectType getNativeObjectType(JSTypeNative typeId) { return (ObjectType) getNativeType(typeId); } public FunctionType getNativeFunctionType(JSTypeNative typeId) { return (FunctionType) getNativeType(typeId); } /** * Looks up a type by name. To allow for forward references to types, an * unrecognized string has to be bound to a NamedType object that will be * resolved later. * * @param scope A scope for doing type name resolution. * @param jsTypeName The name string. * @param sourceName The name of the source file where this reference appears. * @param lineno The line number of the reference. * @return a NamedType if the string argument is not one of the known types, * otherwise the corresponding JSType object. */ public JSType getType(StaticScope<JSType> scope, String jsTypeName, String sourceName, int lineno, int charno) { JSType type = getType(jsTypeName);

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> if (type == null) { // TODO(user): Each instance should support named type creation using // interning. NamedType namedType = new NamedType(this, jsTypeName, sourceName, lineno, charno); unresolvedNamedTypes.put(scope, namedType); type = namedType; } return type; } /** * Flushes out the current resolved and unresolved Named Types from * the type registry. This is intended to be used ONLY before a * compile is run. */ public void clearNamedTypes() { resolvedNamedTypes.clear(); unresolvedNamedTypes.clear(); } /** * Resolve all the unresolved types in the given scope. */ public void resolveTypesInScope(StaticScope<JSType> scope) { for (NamedType type : unresolvedNamedTypes.get(scope)) { type.resolve(reporter, scope); } resolvedNamedTypes.putAll(scope, unresolvedNamedTypes.removeAll(scope)); if (scope != null && scope.getParentScope() == null) { // By default, the global "this" type is just an anonymous object. // If the user has defined a Window type, make the Window the // implicit prototype of "this". PrototypeObjectType globalThis = (PrototypeObjectType) getNativeType( JSTypeNative.GLOBAL_THIS); JSType windowType = getType("Window"); if (globalThis.isUnknownType()) { ObjectType windowObjType = ObjectType.cast(windowType); if (windowObjType != null) { globalThis.setImplicitPrototype(windowObjType); } else { globalThis.setImplicitPrototype( getNativeObjectType(JSTypeNative.OBJECT_TYPE)); } } } } /** * Creates a type representing optional values of the given type. * @return the union of the type and the void type */ public JSType createOptionalType(JSType type) { if (type instanceof UnknownType || type.isAllType()) { return type; } else { return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE)); } } /** * Creates a type representing nullable values of the given type. * @return the union of the type and the Null type */

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> JSType baseType, ImmutableList<JSType> templatizedTypes) { // Only instance object types can currently be templatized; extend this // logic when more types can be templatized. if (baseType instanceof InstanceObjectType) { ObjectType baseObjType = baseType.toObjectType(); return new InstanceObjectType( this, baseObjType.getConstructor(), baseObjType.isNativeObjectType(), templatizedTypes); } else { throw new IllegalArgumentException( "Only instance object types can be templatized"); } } /** * Creates a named type. */ @VisibleForTesting public JSType createNamedType(String reference, String sourceName, int lineno, int charno) { return new NamedType(this, reference, sourceName, lineno, charno); } /** * Identifies the name of a typedef or enum before we actually declare it. */ public void identifyNonNullableName(String name) { Preconditions.checkNotNull(name); nonNullableTypeNames.add(name); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { // If the type expression doesn't contain any names, just // resolve it anyway. boolean hasNames = hasTypeName(n); if (hasNames) { return new UnresolvedTypeExpression(this, n, sourceName); } } return createFromTypeNodesInternal(n, sourceName, scope); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, StaticScope<

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes( n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof ObjectType) && !(nonNullableTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodesInternal( typeList.getLastChild(), sourceName, scope); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodesInternal( typeList.getFirstChild(), sourceName, scope); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; boolean isConstructor = false; Node current = n.getFirstChild(); if (current.getType() == Token.THIS || current.getType() == Token.NEW) { Node contextNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodesInternal( contextNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0( current.getType() == Token.THIS ? "msg.jsdoc.function.thisnotobject" : "msg.jsdoc.function.newnotobject"), sourceName, contextNode.getLineno(), contextNode.getCharno()); } isConstructor = current.getType() == Token.NEW; current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.PARAM_LIST) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope); if (arg.getType()

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> position.setItem(typeNode); position.setHasBrackets(hasLC); position.setPositionInformation(lineno, startCharno, endLineno, endCharno); currentMarker.setType(position); } } /** * Adds a name declaration to the current marker. * @deprecated Use #markName(String, StaticSourceFile, int, int) */ @Deprecated public void markName(String name, int lineno, int charno) { markName(name, null, lineno, charno); } /** * Adds a name declaration to the current marker. */ public void markName(String name, StaticSourceFile file, int lineno, int charno) { if (currentMarker != null) { // Record the name as both a SourcePosition<String> and a // SourcePosition<Node>. The <String> form is deprecated, // because <Node> is more consistent with how other name // references are handled (see #markTypeNode) // // TODO(nicksantos): Remove all uses of the Name position // and replace them with the NameNode position. JSDocInfo.TrimmedStringPosition position = new JSDocInfo.TrimmedStringPosition(); position.setItem(name); position.setPositionInformation(lineno, charno, lineno, charno + name.length()); currentMarker.setName(position); SourcePosition<Node> nodePos = new JSDocInfo.NamePosition(); Node node = Node.newString(Token.NAME, name, lineno, charno); node.setLength(name.length()); node.setStaticSourceFile(file); nodePos.setItem(node); nodePos.setPositionInformation(lineno, charno, lineno, charno + name.length()); currentMarker.setNameNode(nodePos); } } /** * Records a block-level description. * * @return {@code true} if the description was recorded. */ public boolean recordBlockDescription(String description) { populated = true; return currentInfo.documentBlock(description); } /** * Records a visibility. * * @return {@code true} if the visibility was recorded and {@code false} * if it was already defined */

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>ino.jstype.RelationshipVisitor * @return the value returned by the visitor */ abstract <T> T visit(RelationshipVisitor<T> visitor, JSType that); /** * Force this type to resolve, even if the registry is in a lazy * resolving mode. * @see #resolve */ public final JSType forceResolve(ErrorReporter t, StaticScope<JSType> scope) { ResolveMode oldResolveMode = registry.getResolveMode(); registry.setResolveMode(ResolveMode.IMMEDIATE); JSType result = resolve(t, scope); registry.setResolveMode(oldResolveMode); return result; } /** * Resolve this type in the given scope. * * The returned value must be equal to {@code this}, as defined by * {@link #isEquivalentTo}. It may or may not be the same object. This method * may modify the internal state of {@code this}, as long as it does * so in a way that preserves Object equality. * * For efficiency, we should only resolve a type once per compilation job. * For incremental compilations, one compilation job may need the * artifacts from a previous generation, so we will eventually need * a generational flag instead of a boolean one. */ public final JSType resolve(ErrorReporter t, StaticScope<JSType> scope) { if (resolved) { // TODO(nicksantos): Check to see if resolve() looped back on itself. // Preconditions.checkNotNull(resolveResult); if (resolveResult == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return resolveResult; } resolved = true; resolveResult = resolveInternal(t, scope); resolveResult.setResolvedTypeInternal(resolveResult); return resolveResult; } /** * @see #resolve */ abstract JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope); void setResolvedTypeInternal(JSType type) { resolveResult = type; resolved = true; } /** Whether the type has been resolved. */ public final boolean isResolved() { return resolved; } /** Clears the resolved field. */ public final void clearResolved() { resolved = false;

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> resolveResult = null; } /** * A null-safe resolve. * @see #resolve */ static final JSType safeResolve( JSType type, ErrorReporter t, StaticScope<JSType> scope) { return type == null ? null : type.resolve(t, scope); } /** * Certain types have constraints on them at resolution-time. * For example, a type in an {@code @extends} annotation must be an * object. Clients should inject a validator that emits a warning * if the type does not validate, and return false. */ public boolean setValidator(Predicate<JSType> validator) { return validator.apply(this); } public static class TypePair { public final JSType typeA; public final JSType typeB; public TypePair(JSType typeA, JSType typeB) { this.typeA = typeA; this.typeB = typeB; } } /** * A string representation of this type, suitable for printing * in warnings. */ @Override public String toString() { return toStringHelper(false); } /** * A hash code function for diagnosing complicated issues * around type-identity. */ public String toDebugHashCodeString() { return "{" + hashCode() + "}"; } /** * A string representation of this type, suitable for printing * in type annotations at code generation time. */ public final String toAnnotationString() { return toStringHelper(true); } /** * @param forAnnotations Whether this is for use in code generator * annotations. Otherwise, it's for warnings. */ abstract String toStringHelper(boolean forAnnotations); /** * Modify this type so that it matches the specified type. * * This is useful for reverse type-inference, where we want to * infer that an object literal matches its constraint (much like * how the java compiler does reverse-inference to figure out generics). */ public void matchConstraint(JSType constraint) {} }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> @Override public JSType getLeastSupertype(JSType that) { throw new UnsupportedOperationException(); } @Override public JSType getGreatestSubtype(JSType that) { throw new UnsupportedOperationException(); } @Override public TernaryValue testForEquality(JSType that) { throw new UnsupportedOperationException(); } @Override public <T> T visit(Visitor<T> visitor) { throw new UnsupportedOperationException(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { throw new UnsupportedOperationException(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { returnType = safeResolve(returnType, t, scope); if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { paramNode.setJSType(paramNode.getJSType().resolve(t, scope)); } } return this; } boolean hasUnknownParamsOrReturn() { if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { JSType type = paramNode.getJSType(); if (type == null || type.isUnknownType()) { return true; } } } return returnType == null || returnType.isUnknownType(); } @Override String toStringHelper(boolean forAnnotations) { return "[ArrowType]"; } @Override public boolean hasAnyTemplateTypesInternal() { return returnType.hasAnyTemplateTypes() || hasTemplatedParameterType(); } private boolean hasTemplatedParameterType() { if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { JSType type = paramNode.getJSType(); if (type != null && type.hasAnyTemplateTypes()) { return true; } } } return false; } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> set the LHS of an assign with a typed RHS function.) node.setJSType(type); } void resolve(Scope scope) { node.setJSType(type.resolve(typeParsingErrorReporter, scope)); } } TypedScopeCreator(AbstractCompiler compiler) { this(compiler, compiler.getCodingConvention()); } TypedScopeCreator(AbstractCompiler compiler, CodingConvention codingConvention) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.codingConvention = codingConvention; this.typeRegistry = compiler.getTypeRegistry(); this.typeParsingErrorReporter = typeRegistry.getErrorReporter(); this.unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); } /** * Creates a scope with all types declared. Declares newly discovered types * and type properties in the type registry. */ @Override public Scope createScope(Node root, Scope parent) { // Constructing the global scope is very different than constructing // inner scopes, because only global scopes can contain named classes that // show up in the type registry. Scope newScope = null; AbstractScopeBuilder scopeBuilder = null; if (parent == null) { JSType globalThis = typeRegistry.getNativeObjectType(JSTypeNative.GLOBAL_THIS); // Mark the main root, the externs root, and the src root // with the global this type. root.setJSType(globalThis); root.getFirstChild().setJSType(globalThis); root.getLastChild().setJSType(globalThis); // Run a first-order analysis over the syntax tree. (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults)) .process(root.getFirstChild(), root.getLastChild()); // Find all the classes in the global scope. newScope = createInitialScope(root); GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope); scopeBuilder = globalScopeBuilder; NodeTraversal.traverse(compiler, root, scopeBuilder); } else { newScope = new Scope(parent, root); LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope); scopeBuilder = localScopeBuilder; localScopeBuilder.build(); } scopeBuilder.resolveStubDeclarations(); scopeBuilder

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.resolveTypes(); // Gather the properties in each function that we found in the // global scope, if that function has a @this type that we can // build properties on. for (Node functionNode : scopeBuilder.nonExternFunctions) { JSType type = functionNode.getJSType(); if (type != null && type.isFunctionType()) { FunctionType fnType = type.toMaybeFunctionType(); JSType fnThisType = fnType.getTypeOfThis(); if (!fnThisType.isUnknownType()) { NodeTraversal.traverse(compiler, functionNode.getLastChild(), scopeBuilder.new CollectProperties(fnThisType)); } } } if (parent == null) { codingConvention.defineDelegateProxyPrototypeProperties( typeRegistry, newScope, delegateProxyPrototypes, delegateCallingConventions); } return newScope; } /** * Patches a given global scope by removing variables previously declared in * a script and re-traversing a new version of that script. * * @param globalScope The global scope generated by {@code createScope}. * @param scriptRoot The script that is modified. */ void patchGlobalScope(Scope globalScope, Node scriptRoot) { // Preconditions: This is supposed to be called only on (named) SCRIPT nodes // and a global typed scope should have been generated already. Preconditions.checkState(scriptRoot.isScript()); Preconditions.checkNotNull(globalScope); Preconditions.checkState(globalScope.isGlobal()); String scriptName = NodeUtil.getSourceName(scriptRoot); Preconditions.checkNotNull(scriptName); for (Node node : ImmutableList.copyOf(functionAnalysisResults.keySet())) { if (scriptName.equals(NodeUtil.getSourceName(node))) { functionAnalysisResults.remove(node); } } (new FirstOrderFunctionAnalyzer( compiler, functionAnalysisResults)).process(null, scriptRoot); // TODO(bashir): Variable declaration is not the only side effect of last // global scope generation but here we only wipe that part off! // Remove all variables that were previously declared in this scripts. // First find all vars to remove then remove them because of iterator! Iterator<Var> varIter = globalScope.getVars(); List<Var>

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> varsToRemove = Lists.newArrayList(); while (varIter.hasNext()) { Var oldVar = varIter.next(); if (scriptName.equals(oldVar.getInputName())) { varsToRemove.add(oldVar); } } for (Var var : varsToRemove) { globalScope.undeclare(var); globalScope.getTypeOfThis().toObjectType().removeProperty(var.getName()); } // Now re-traverse the given script. GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope); NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder); } /** * Create the outermost scope. This scope contains native binding such as * {@code Object}, {@code Date}, etc. */ @VisibleForTesting Scope createInitialScope(Node root) { NodeTraversal.traverse( compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry)); Scope s = Scope.createGlobalScope(root); declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE); declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, DATE_FUNCTION_TYPE); declareNativeFunctionType(s, ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE); declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE); declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); declareNativeValueType(s, "undefined", VOID_TYPE); // There is no longer a need to special case ActiveXObject // but this remains here until we can get the extern forks // cleaned up. declareNativeValueType(s, "ActiveXObject", FUNCTION_INSTANCE_TYPE); return s; } private void declareNativeFunctionType

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(Scope scope, JSTypeNative tId) { FunctionType t = typeRegistry.getNativeFunctionType(tId); declareNativeType(scope, t.getInstanceType().getReferenceName(), t); declareNativeType( scope, t.getPrototype().getReferenceName(), t.getPrototype()); } private void declareNativeValueType(Scope scope, String name, JSTypeNative tId) { declareNativeType(scope, name, typeRegistry.getNativeType(tId)); } private void declareNativeType(Scope scope, String name, JSType t) { scope.declare(name, null, t, null, false); } private static class DiscoverEnumsAndTypedefs extends AbstractShallowStatementCallback { private final JSTypeRegistry registry; DiscoverEnumsAndTypedefs(JSTypeRegistry registry) { this.registry = registry; } @Override public void visit(NodeTraversal t, Node node, Node parent) { Node nameNode = null; switch (node.getType()) { case Token.VAR: for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { identifyNameNode( child, child.getFirstChild(), NodeUtil.getBestJSDocInfo(child)); } break; case Token.EXPR_RESULT: Node firstChild = node.getFirstChild(); if (firstChild.isAssign()) { identifyNameNode( firstChild.getFirstChild(), firstChild.getLastChild(), firstChild.getJSDocInfo()); } else { identifyNameNode( firstChild, null, firstChild.getJSDocInfo()); } break; } } private void identifyNameNode( Node nameNode, Node valueNode, JSDocInfo info) { if (nameNode.isQualifiedName()) { if (info != null) { if (info.hasEnumParameterType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.hasTypedefType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } } } } private JSType getNativeType(JSTypeNative nativeType) { return typeRegistry.getNativeType(nativeType); } private abstract class

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> AbstractScopeBuilder implements NodeTraversal.Callback { /** * The scope that we're building. */ final Scope scope; private final List<DeferredSetType> deferredSetTypes = Lists.newArrayList(); /** * Functions that we found in the global scope and not in externs. */ private final List<Node> nonExternFunctions = Lists.newArrayList(); /** * Object literals with a @lends annotation aren't analyzed until we * reach the root of the statement they're defined in. * * This ensures that if there are any @lends annotations on the object * literals, the type on the @lends annotation resolves correctly. * * For more information, see * http://code.google.com/p/closure-compiler/issues/detail?id=314 */ private List<Node> lentObjectLiterals = null; /** * Type-less stubs. * * If at the end of traversal, we still don't have types for these * stubs, then we should declare UNKNOWN types. */ private final List<StubDeclaration> stubDeclarations = Lists.newArrayList(); /** * The current source file that we're in. */ private String sourceName = null; /** * The InputId of the current node. */ private InputId inputId; private AbstractScopeBuilder(Scope scope) { this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator<Var> vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(t, n); switch (n.getType()) { case Token.CALL: checkForClassDefiningCalls(t, n, parent); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n); break; case Token.VAR: defineVar(n); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() && n.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null); } break; }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // Analyze any @lends object literals in this statement. if (n.getParent() != null && NodeUtil.isStatement(n) && lentObjectLiterals != null) { for (Node objLit : lentObjectLiterals) { defineObjectLiteral(objLit); } lentObjectLiterals.clear(); } } private void attachLiteralTypes(NodeTraversal t, Node n) { switch (n.getType()) { case Token.NULL: n.setJSType(getNativeType(NULL_TYPE)); break; case Token.VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.STRING: n.setJSType(getNativeType(STRING_TYPE)); break; case Token.NUMBER: n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.OBJECTLIT: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.getLendsName() != null) { if (lentObjectLiterals == null) { lentObjectLiterals = Lists.newArrayList(); } lentObjectLiterals.add(n); } else { defineObjectLiteral(n); } break; // NOTE(nicksantos): If we ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(Node objectLit) { // Handle the @lends annotation. JSType type = null; JSDocInfo info = objectLit.getJSDocInfo(); if (info != null && info.getLendsName() != null) { String lendsName = info.getLendsName(); Var lendsVar = scope.getVar(lendsName); if (lendsVar == null) { compiler.report( JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); } else

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>EnumType() ? objLitType.toMaybeEnumType().getElementsType() : NodeUtil.getObjectLitKeyTypeFromValueType(keyNode, valueType); // Try to declare this property in the current scope if it // has an authoritative name. String qualifiedName = NodeUtil.getBestLValueName(keyNode); if (qualifiedName != null) { boolean inferred = keyType == null; defineSlot(keyNode, objLit, qualifiedName, keyType, inferred); } else if (keyType != null) { setDeferredType(keyNode, keyType); } if (keyType != null && objLitType != null && declareOnOwner) { // Declare this property on its object literal. boolean isExtern = keyNode.isFromExterns(); objLitType.defineDeclaredProperty(memberName, keyType, keyNode); } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.isGetProp() ? node.getFirstChild() : NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's OK to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>SDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(sourceName, info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? unknownType : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is inferred, and we're in a local * scope, we should assume the whole function is inferred. */ private boolean shouldUseFunctionLiteralType( FunctionType type, JSDocInfo info, Node lValue) { if (info != null) { return true; } if (lValue != null && NodeUtil.isObjectLitKey(lValue, lValue.getParent())) { return false; } return scope.isGlobal() || !type.isReturnTypeInferred(); } /** * Creates a new function type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - A function literal that needs a type attached to it. * - An assignment expression with function-type info in the JsDoc. * * All parameters are optional, and we will do the best we can to create * a function type. * * This function will always create a function type, so only call it if * you're sure that's what you want. * * @param rValue The function node. * @param name the function's name * @param info the {@link JSDocInfo} attached to the function definition * @param lvalueNode The node where this function is being * assigned. For example, {@code A.prototype.foo = ...} would be used to * determine that this function is a method of A.prototype. May be * null to indicate that this is not being

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> assigned to a qualified name. */ private FunctionType createFunctionTypeFromNodes( @Nullable Node rValue, @Nullable String name, @Nullable JSDocInfo info, @Nullable Node lvalueNode) { FunctionType functionType = null; // Global ctor aliases should be registered with the type registry. if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) { Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() != null && var.getType().isFunctionType()) { FunctionType aliasedType = var.getType().toMaybeFunctionType(); if ((aliasedType.isConstructor() || aliasedType.isInterface()) && !aliasedType.isNativeObjectType()) { functionType = aliasedType; if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, functionType.getInstanceType()); } } } } if (functionType == null) { Node errorRoot = rValue == null ? lvalueNode : rValue; boolean isFnLiteral = rValue != null && rValue.isFunction(); Node fnRoot = isFnLiteral ? rValue : null; Node parametersNode = isFnLiteral ? rValue.getFirstChild().getNext() : null; Node fnBlock = isFnLiteral ? parametersNode.getNext() : null; if (info != null && info.hasType()) { JSType type = info.getType().evaluate(scope, typeRegistry); // Known to be not null since we have the FUNCTION token there. type = type.restrictByNotNullOrUndefined(); if (type.isFunctionType()) { functionType = type.toMaybeFunctionType(); functionType.setJSDocInfo(info); } } if (functionType == null) { // Find the type of any overridden function. Node ownerNode = NodeUtil.getBestLValueOwner(lvalueNode); String ownerName = NodeUtil.getBestLValueName(ownerNode); Var ownerVar = null; String propName = null; ObjectType ownerType = null; if (ownerName != null) { ownerVar = scope.getVar(ownerName); if (ownerVar != null) { ownerType

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = ObjectType.cast(ownerVar.getType()); } if (name != null) { propName = name.substring(ownerName.length() + 1); } } FunctionType overriddenType = null; if (ownerType != null && propName != null) { overriddenType = findOverriddenFunction(ownerType, propName); } FunctionTypeBuilder builder = new FunctionTypeBuilder(name, compiler, errorRoot, sourceName, scope) .setContents(getFunctionAnalysisResults(fnRoot)) .inferFromOverriddenFunction(overriddenType, parametersNode) .inferTemplateTypeName(info) .inferReturnType(info) .inferInheritance(info); // Infer the context type. boolean searchedForThisType = false; if (ownerType != null && ownerType.isFunctionPrototypeType() && ownerType.getOwnerFunction().hasInstanceType()) { builder.inferThisType( info, ownerType.getOwnerFunction().getInstanceType()); searchedForThisType = true; } else if (ownerNode != null && ownerNode.isThis()) { // If 'this' has a type, use that instead. // This is a hack, necessary because CollectProperties (below) // doesn't run with the scope that it's building, // so scope.getTypeOfThis() will be wrong. JSType injectedThisType = ownerNode.getJSType(); builder.inferThisType( info, injectedThisType == null ? scope.getTypeOfThis() : injectedThisType); searchedForThisType = true; } if (!searchedForThisType) { builder.inferThisType(info); } functionType = builder .inferParameterTypes(parametersNode, info) .buildAndRegister(); } } // all done return functionType; } /** * Find the function that's being overridden on this type, if any. */ private FunctionType findOverriddenFunction( ObjectType ownerType, String propName) { // First, check to see if the property is implemented // on a superclass. JSType propType = ownerType.getPropertyType(propName); if (propType != null && propType.isFunctionType()) { return propType.toMaybeFunctionType(); } else {

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // If it's not, then check to see if it's implemented // on an implemented interface. for (ObjectType iface : ownerType.getCtorImplementedInterfaces()) { propType = iface.getPropertyType(propName); if (propType != null && propType.isFunctionType()) { return propType.toMaybeFunctionType(); } } } return null; } /** * Creates a new enum type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - An object literal that needs an enum type attached to it. * - An assignment expression with an enum tag in the JsDoc. * * This function will always create an enum type, so only call it if * you're sure that's what you want. * * @param rValue The node of the enum. * @param name The enum's name * @param info The {@link JSDocInfo} attached to the enum definition. * @param lValueNode The node where this function is being * assigned. */ private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info, Node lValueNode) { Preconditions.checkNotNull(info); Preconditions.checkState(info.hasEnumParameterType()); EnumType enumType = null; if (rValue != null && rValue.isQualifiedName()) { // Handle an aliased enum. Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } if (enumType == null) { JSType elementsType = info.getEnumParameterType().evaluate(scope, typeRegistry); enumType = typeRegistry.createEnumType(name, rValue, elementsType); if (rValue != null && rValue.isObjectLit()) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>, ENUM_NOT_CONSTANT, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, enumType.getElementsType()); } return enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is * inferred. * * Slots may be any variable or any qualified name in the global scope. * * @param n the defining NAME or GETPROP node. * @param parent the {@code n}'s parent. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. */ void defineSlot(Node n, Node parent, JSType type, boolean inferred) { Preconditions.checkArgument(inferred || type != null); // Only allow declarations of NAMEs and qualified names. // Object literal keys will have to compute their names themselves. if (n.isName()) { Preconditions.checkArgument( parent.isFunction() || parent.isVar() || parent.isParamList() || parent.isCatch()); } else { Preconditions.checkArgument( n.isGetProp() && (parent.isAssign() || parent.isExprResult())); } defineSlot

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>(n, parent, n.getQualifiedName(), type, inferred); } /** * Defines a symbol in the current scope. * * @param n the defining NAME or GETPROP or object literal key node. * @param parent the {@code n}'s parent. * @param variableName The name that this should be known by. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. * @param inferred Whether the type is inferred or declared. */ void defineSlot(Node n, Node parent, String variableName, JSType type, boolean inferred) { Preconditions.checkArgument(!variableName.isEmpty()); boolean isGlobalVar = n.isName() && scope.isGlobal(); boolean shouldDeclareOnGlobalThis = isGlobalVar && (parent.isVar() || parent.isFunction()); // If n is a property, then we should really declare it in the // scope where the root object appears. This helps out people // who declare "global" names in an anonymous namespace. Scope scopeToDeclareIn = scope; if (n.isGetProp() && !scope.isGlobal() && isQnameRootedInGlobalScope(n)) { Scope globalScope = scope.getGlobalScope(); // don't try to declare in the global scope if there's // already a symbol there with this name. if (!globalScope.isDeclared(variableName, false)) { scopeToDeclareIn = scope.getGlobalScope(); } } // The input may be null if we are working with a AST snippet. So read // the extern info from the node. boolean isExtern = n.isFromExterns(); Var newVar = null; // declared in closest scope? CompilerInput input = compiler.getInput(inputId); if (scopeToDeclareIn.isDeclared(variableName, false)) { Var oldVar = scopeToDeclareIn.getVar(variableName); newVar = validator.expectUndeclaredVariable( sourceName, input, n, parent, oldVar, variableName, type); } else { if (type != null) { setDeferredType(n, type); } newVar = scopeToDeclareIn.declare(variableName,

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> n, type, input, inferred); if (type instanceof EnumType) { Node initialValue = newVar.getInitialValue(); boolean isValidValue = initialValue != null && (initialValue.isObjectLit() || initialValue.isQualifiedName()); if (!isValidValue) { compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER)); } } } // We need to do some additional work for constructors and interfaces. FunctionType fnType = JSType.toMaybeFunctionType(type); if (fnType != null && // We don't want to look at empty function types. !type.isEmptyType()) { // We want to make sure that when we declare a new instance type // (with @constructor) that there's actually a ctor for it. // This doesn't apply to structural constructors (like // function(new:Array). Checking the constructed type against // the variable name is a sufficient check for this. if ((fnType.isConstructor() || fnType.isInterface()) && variableName.equals(fnType.getReferenceName())) { finishConstructorDefinition(n, variableName, fnType, scopeToDeclareIn, input, newVar); } } if (shouldDeclareOnGlobalThis) { ObjectType globalThis = typeRegistry.getNativeObjectType(GLOBAL_THIS); if (inferred) { globalThis.defineInferredProperty(variableName, type == null ? getNativeType(JSTypeNative.NO_TYPE) : type, n); } else { globalThis.defineDeclaredProperty(variableName, type, n); } } if (isGlobalVar && "Window".equals(variableName) && type != null && type.isFunctionType() && type.isConstructor()) { FunctionType globalThisCtor = typeRegistry.getNativeObjectType(GLOBAL_THIS).getConstructor(); globalThisCtor.getInstanceType().clearCachedValues(); globalThisCtor.getPrototype().clearCachedValues(); globalThisCtor .setPrototypeBasedOn((type.toMaybeFunctionType()).getInstanceType()); } } private void finishConstructorDefinition( Node n, String variableName, FunctionType fnType, Scope scopeToDeclareIn, CompilerInput input, Var newVar) { //

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> Declare var.prototype in the scope chain. FunctionType superClassCtor = fnType.getSuperClassConstructor(); Property prototypeSlot = fnType.getSlot("prototype"); // When we declare the function prototype implicitly, we // want to make sure that the function and its prototype // are declared at the same node. We also want to make sure // that the if a symbol has both a Var and a JSType, they have // the same node. // // This consistency is helpful to users of SymbolTable, // because everything gets declared at the same place. prototypeSlot.setNode(n); String prototypeName = variableName + ".prototype"; // There are some rare cases where the prototype will already // be declared. See TypedScopeCreatorTest#testBogusPrototypeInit. // Fortunately, other warnings will complain if this happens. Var prototypeVar = scopeToDeclareIn.getVar(prototypeName); if (prototypeVar != null && prototypeVar.scope == scopeToDeclareIn) { scopeToDeclareIn.undeclare(prototypeVar); } scopeToDeclareIn.declare(prototypeName, n, prototypeSlot.getType(), input, /* declared iff there's an explicit supertype */ superClassCtor == null || superClassCtor.getInstanceType().isEquivalentTo( getNativeType(OBJECT_TYPE))); // Make sure the variable is initialized to something if // it constructs itself. if (newVar.getInitialValue() == null && !n.isFromExterns()) { compiler.report( JSError.make(sourceName, n, fnType.isConstructor() ? CTOR_INITIALIZER : IFACE_INITIALIZER, variableName)); } } /** * Check if the given node is a property of a name in the global scope. */ private boolean isQnameRootedInGlobalScope(Node n) { Scope scope = getQnameRootScope(n); return scope != null && scope.isGlobal(); } /** * Return the scope for the name of the given node. */ private Scope getQnameRootScope(Node n) { Node root = NodeUtil.getRootOfQualifiedName(n); if (root.isName()) { Var var = scope.getVar(root.getString()); if

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> (var != null) { return var.getScope(); } } return null; } /** * Look for a type declaration on a property assignment * (in an ASSIGN or an object literal key). * * @param info The doc info for this property. * @param lValue The l-value node. * @param rValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ private JSType getDeclaredType(String sourceName, JSDocInfo info, Node lValue, @Nullable Node rValue) { if (info != null && info.hasType()) { return getDeclaredTypeInAnnotation(sourceName, lValue, info); } else if (rValue != null && rValue.isFunction() && shouldUseFunctionLiteralType( JSType.toMaybeFunctionType(rValue.getJSType()), info, lValue)) { return rValue.getJSType(); } else if (info != null) { if (info.hasEnumParameterType()) { if (rValue != null && rValue.isObjectLit()) { return rValue.getJSType(); } else { return createEnumTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } } else if (info.isConstructor() || info.isInterface()) { return createFunctionTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } else { // Check if this is constant, and if it has a known type. if (info.isConstant()) { JSType knownType = null; if (rValue != null) { JSDocInfo rValueInfo = rValue.getJSDocInfo(); if (rValueInfo != null && rValueInfo.hasType()) { // If rValue has a type-cast, we use the type in the type-cast. return rValueInfo.getType().evaluate(scope, typeRegistry); } else if (rValue.getJSType() != null && !rValue.getJSType().isUnknownType()) { // If rValue's type was already computed during scope creation, // then we can safely use that. return rValue.get

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>JSType(); } else if (rValue.isOr()) { // Check for a very specific JS idiom: // var x = x || TYPE; // This is used by Closure's base namespace for esoteric // reasons. Node firstClause = rValue.getFirstChild(); Node secondClause = firstClause.getNext(); boolean namesMatch = firstClause.isName() && lValue.isName() && firstClause.getString().equals(lValue.getString()); if (namesMatch && secondClause.getJSType() != null && !secondClause.getJSType().isUnknownType()) { return secondClause.getJSType(); } } } } } } return getDeclaredTypeInAnnotation(sourceName, lValue, info); } private FunctionType getFunctionType(@Nullable Var v) { JSType t = v == null ? null : v.getType(); ObjectType o = t == null ? null : t.dereference(); return JSType.toMaybeFunctionType(o); } /** * Look for calls that set a delegate method's calling convention. */ private void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions) { codingConvention.checkForCallingConventionDefiningCalls(n, delegateCallingConventions); } /** * Look for class-defining calls. * Because JS has no 'native' syntax for defining classes, * this is often very coding-convention dependent and business-logic heavy. */ private void checkForClassDefiningCalls( NodeTraversal t, Node n, Node parent) { SubclassRelationship relationship = codingConvention.getClassesDefinedByCall(n); if (relationship != null) { FunctionType superCtor = getFunctionType( scope.getVar(relationship.superclassName)); FunctionType subCtor = getFunctionType( scope.getVar(relationship.subclassName)); if (superCtor != null && superCtor.isConstructor() && subCtor != null && subCtor.isConstructor()) { ObjectType superClass = superCtor.getInstanceType(); ObjectType subClass = subCtor.getInstanceType(); // superCtor and subCtor might be structural constructors // (like {function(new:Object)}) so we need to resolve them back // to

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>codingConvention.getDelegateSuperclassName())); if (delegatorObject != null && delegateBaseObject != null && delegateSuperObject != null) { FunctionType delegatorCtor = delegatorObject.getConstructor(); FunctionType delegateBaseCtor = delegateBaseObject.getConstructor(); FunctionType delegateSuperCtor = delegateSuperObject.getConstructor(); if (delegatorCtor != null && delegateBaseCtor != null && delegateSuperCtor != null) { FunctionParamBuilder functionParamBuilder = new FunctionParamBuilder(typeRegistry); functionParamBuilder.addRequiredParams( getNativeType(U2U_CONSTRUCTOR_TYPE)); FunctionType findDelegate = typeRegistry.createFunctionType( typeRegistry.createDefaultObjectUnion(delegateBaseObject), functionParamBuilder.build()); FunctionType delegateProxy = typeRegistry.createConstructorType( delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX, null, null, null, null); delegateProxy.setPrototypeBasedOn(delegateBaseObject); codingConvention.applyDelegateRelationship( delegateSuperObject, delegateBaseObject, delegatorObject, delegateProxy, findDelegate); delegateProxyPrototypes.add(delegateProxy.getPrototype()); } } } /** * Declare the symbol for a qualified name in the global scope. * * @param info The doc info for this property. * @param n A top-level GETPROP node (it should not be contained inside * another GETPROP). * @param parent The parent of {@code n}. * @param rhsValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { Node ownerNode = n.getFirstChild(); String ownerName = ownerNode.getQualifiedName(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); Preconditions.checkArgument(qName != null && ownerName != null); // Precedence of type information on GETPROPs: // 1) @type annotation / @enum annotation // 2) ASSIGN to FUNCTION literal // 3) @param/@return annotation (with no function literal)

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> // 4) ASSIGN to something marked @const // 5) ASSIGN to anything else // // 1, 3, and 4 are declarations, 5 is inferred, and 2 is a declaration iff // the function has JsDoc or has not been declared before. // // FUNCTION literals are special because TypedScopeCreator is very smart // about getting as much type information as possible for them. // Determining type for #1 + #2 + #3 + #4 JSType valueType = getDeclaredType(t.getSourceName(), info, n, rhsValue); if (valueType == null && rhsValue != null) { // Determining type for #5 valueType = rhsValue.getJSType(); } // Function prototypes are special. // It's a common JS idiom to do: // F.prototype = { ... }; // So if F does not have an explicitly declared super type, // allow F.prototype to be redefined arbitrarily. if ("prototype".equals(propName)) { Var qVar = scope.getVar(qName); if (qVar != null) { // If the programmer has declared that F inherits from Super, // and they assign F.prototype to an object literal, // then they are responsible for making sure that the object literal's // implicit prototype is set up appropriately. We just obey // the @extends tag. ObjectType qVarType = ObjectType.cast(qVar.getType()); if (qVarType != null && rhsValue != null && rhsValue.isObjectLit()) { typeRegistry.resetImplicitPrototype( rhsValue.getJSType(), qVarType.getImplicitPrototype()); } else if (!qVar.isTypeInferred()) { // If the programmer has declared that F inherits from Super, // and they assign F.prototype to some arbitrary expression, // there's not much we can do. We just ignore the expression, // and hope they've annotated their code in a way to tell us // what props are going to be on that prototype. return; } if (qVar.getScope() == scope) { scope.undeclare(qVar); } } } if (valueType == null) { if (parent.isExprResult())

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> * goog.bar(); * /* Reset goog.bar to a no-op. / * goog.bar = function() {}; * } * * In a dynamic language with first-class functions, it's very difficult * to know which one the user intended without looking at lots of * contextual information (the second example demonstrates a small case * of this, but there are some really pathological cases as well). * * The current algorithm checks if either the declaration has * JsDoc type information, or @const with a known type, * or a function literal with a name we haven't seen before. */ private boolean isQualifiedNameInferred( String qName, Node n, JSDocInfo info, Node rhsValue, JSType valueType) { if (valueType == null) { return true; } boolean inferred = true; if (info != null) { inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } if (inferred && rhsValue != null && rhsValue.isFunction()) { if (info != null) { return false; } else if (!scope.isDeclared(qName, false) && n.isUnscopedQualifiedName()) { // Check if this is in a conditional block. // Functions assigned in conditional blocks are inferred. for (Node current = n.getParent(); !(current.isScript() || current.isFunction()); current = current.getParent()) { if (NodeUtil.isControlStructure(current)) { return true; } } // Check if this is assigned in an inner scope. // Functions assigned in inner scopes are inferred. AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents == null || !contents.getEscapedQualifiedNames().contains(qName)) { return false; } } } return inferred; } /** * Find the ObjectType associated with the given slot. * @param slotName The name of the slot to find the type in. * @return An object type, or null if this slot does not contain an object.

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> */ private ObjectType getObjectSlot(String slotName) { Var ownerVar = scope.getVar(slotName); if (ownerVar != null) { JSType ownerVarType = ownerVar.getType(); return ObjectType.cast(ownerVarType == null ? null : ownerVarType.restrictByNotNullOrUndefined()); } return null; } /** * Resolve any stub declarations to unknown types if we could not * find types for them during traversal. */ void resolveStubDeclarations() { for (StubDeclaration stub : stubDeclarations) { Node n = stub.node; Node parent = n.getParent(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); String ownerName = stub.ownerName; boolean isExtern = stub.isExtern; if (scope.isDeclared(qName, false)) { continue; } // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); defineSlot(n, parent, unknownType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( propName, unknownType, n); } else { typeRegistry.registerPropertyOnType( propName, ownerType == null ? unknownType : ownerType); } } } /** * Collects all declared properties in a function, and * resolves them relative to the global scope. */ private final class CollectProperties extends AbstractShallowStatementCallback { private final JSType thisType; CollectProperties(JSType thisType) { this.thisType = thisType; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isExprResult()) { Node child = n.getFirstChild(); switch (child.getType()) { case Token.ASSIGN: maybeCollectMember(t, child.getFirstChild(), child, child.getLastChild()); break; case Token.GETPROP: maybeCollectMember(t, child

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>, child, null); break; } } } private void maybeCollectMember(NodeTraversal t, Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no JSDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || !member.isGetProp() || !member.getFirstChild().isThis()) { return; } member.getFirstChild().setJSType(thisType); JSType jsType = getDeclaredType(t.getSourceName(), info, member, value); Node name = member.getLastChild(); if (jsType != null && (name.isName() || name.isString()) && thisType.toObjectType() != null) { thisType.toObjectType().defineDeclaredProperty( name.getString(), jsType, member); } } } // end CollectProperties } /** * A stub declaration without any type information. */ private static final class StubDeclaration { private final Node node; private final boolean isExtern; private final String ownerName; private StubDeclaration(Node node, boolean isExtern, String ownerName) { this.node = node; this.isExtern = isExtern; this.ownerName = ownerName; } } /** * A shallow traversal of the global scope to build up all classes, * functions, and methods. */ private final class GlobalScopeBuilder extends AbstractScopeBuilder { private GlobalScopeBuilder(Scope scope) { super(scope); } /** * Visit a node in the global scope, and add anything it declares to the * global symbol table. * * @param t The current traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); switch (n.getType()) { case Token.VAR: // Handle typedefs. if (n.hasOneChild())

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> { checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); } break; } } @Override void maybeDeclareQualifiedName( NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { checkForTypedef(t, n, info); super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue); } /** * Handle typedefs. * @param t The current traversal. * @param candidate A qualified name node. * @param info JSDoc comments. */ private void checkForTypedef( NodeTraversal t, Node candidate, JSDocInfo info) { if (info == null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recursive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, unknownType); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're building. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } for (Multiset.Entry<String> entry : contents.getAssignedNameCounts().entrySet()) { Var v = scope.getVar(entry.getElement()); Preconditions.checkState(v.getScope() == scope); if (entry.getCount() == 1) { v.markAssignedExactlyOnce(); } } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.isParamList() && parent == scope.getRootNode()) { handleFunctionInputs(parent); return; } super.visit(t, n, parent); } /** Handle bleeding functions and function parameters. */ private void handleFunctionInputs(Node fnNode) { // Handle bleeding functions. Node fnNameNode = fnNode.getFirstChild(); String fnName = fnNameNode.getString(); if (!fnName.isEmpty()) { Scope.Var fnVar = scope.getVar(fnName); if (fnVar == null || // Make sure we're not touching a native function. Native // functions aren't bleeding, but may not have a declaration // node. (fnVar.getNameNode() != null && // Make sure that the function is actually bleeding by checking // if has already been declared. fnVar.getInitialValue() != fnNode)) { defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false); } } declareArguments(fnNode); } /** * Declares all of a function's arguments. */ private void declareArguments(Node functionNode) { Node astParameters = functionNode.getFirstChild().getNext(); Node iifeArgumentNode = null; if (NodeUtil.isCallOrNewTarget(functionNode)) { iifeArgumentNode

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> = functionNode.getNext(); } Node body = astParameters.getNext(); FunctionType functionType = JSType.toMaybeFunctionType(functionNode.getJSType()); if (functionType != null) { Node jsDocParameters = functionType.getParametersNode(); if (jsDocParameters != null) { Node jsDocParameter = jsDocParameters.getFirstChild(); for (Node astParameter : astParameters.children()) { JSType paramType = jsDocParameter == null ? unknownType : jsDocParameter.getJSType(); boolean inferred = paramType == null || paramType == unknownType; if (iifeArgumentNode != null && inferred) { String argumentName = iifeArgumentNode.getQualifiedName(); Var argumentVar = argumentName == null || scope.getParent() == null ? null : scope.getParent().getVar(argumentName); if (argumentVar != null && !argumentVar.isTypeInferred()) { paramType = argumentVar.getType(); } } if (paramType == null) { paramType = unknownType; } defineSlot(astParameter, functionNode, paramType, inferred); if (jsDocParameter != null) { jsDocParameter = jsDocParameter.getNext(); } if (iifeArgumentNode != null) { iifeArgumentNode = iifeArgumentNode.getNext(); } } } } } // end declareArguments } // end LocalScopeBuilder /** * Does a first-order function analysis that just looks at simple things * like what variables are escaped, and whether 'this' is used. */ private static class FirstOrderFunctionAnalyzer extends AbstractScopedCallback implements CompilerPass { private final AbstractCompiler compiler; private final Map<Node, AstFunctionContents> data; FirstOrderFunctionAnalyzer( AbstractCompiler compiler, Map<Node, AstFunctionContents> outParam) { this.compiler = compiler; this.data = outParam; } @Override public void process(Node externs, Node root) { if (externs == null) { NodeTraversal.traverse(compiler, root, this); } else { NodeTraversal.traverseRoots( compiler, ImmutableList.of(externs, root), this); } } @Override public void enterScope(NodeTraversal

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); data.put(n, new AstFunctionContents(n)); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { return; } if (n.isReturn() && n.getFirstChild() != null) { data.get(t.getScopeRoot()).recordNonEmptyReturn(); } if (t.getScopeDepth() <= 1) { // The first-order function analyzer looks at two types of variables: // // 1) Local variables that are assigned in inner scopes ("escaped vars") // // 2) Local variables that are assigned more than once. // // We treat all global variables as escaped by default, so there's // no reason to do this extra computation for them. return; } if (n.isName() && NodeUtil.isLValue(n) && // Be careful of bleeding functions, which create variables // in the inner scope, not the scope where the name appears. !NodeUtil.isBleedingFunctionName(n)) { String name = n.getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordAssignedName(name); } if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordEscapedVarName(name); } } } else if (n.isGetProp() && n.isUnscopedQualifiedName() && NodeUtil.isLValue(n)) { String name = NodeUtil.getRootOfQualifiedName(n).getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()) .recordEscapedQualifiedName(n.getQualifiedName()); } } }

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.javascript.jscomp.graph.GraphvizGraph; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; /** * Pass factories and meta-data for native Compiler passes. * * @author nicksantos@google.com (Nick Santos) */ public abstract class PassConfig { // Used by subclasses in this package. final CompilerOptions options; /** * A memoized version of scopeCreator. It must be memoized so that * we can make two separate passes over the AST, one for inferring types * and one for checking types. */ private MemoizedScopeCreator typedScopeCreator; /** * This is the scope creator that {@code TypedScopeCreator} delegates to. */ private TypedScopeCreator internalScopeCreator; /** The global typed scope. */ Scope topScope = null; public PassConfig(CompilerOptions options) { this.options = options; } /** * Regenerates the top scope from scratch. * * @param compiler The compiler for which the global scope is regenerated. * @param root The root of the AST. */ void regenerateGlobalTypedScope(AbstractCompiler compiler, Node root) { internalScopeCreator = new TypedScope

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>Creator(compiler); typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator); topScope = typedScopeCreator.createScope(root, null); } void clearTypedScope() { internalScopeCreator = null; typedScopeCreator = null; topScope = null; } /** * Regenerates the top scope potentially only for a sub-tree of AST and then * copies information for the old global scope. * * @param compiler The compiler for which the global scope is generated. * @param scriptRoot The root of the AST used to generate global scope. */ void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) { Preconditions.checkNotNull(internalScopeCreator); internalScopeCreator.patchGlobalScope(topScope, scriptRoot); } /** * Gets the scope creator for typed scopes. */ MemoizedScopeCreator getTypedScopeCreator() { return typedScopeCreator; } /** * Gets the global scope, with type information. */ Scope getTopScope() { return topScope; } /** * Gets the checking passes to run. * * Checking passes revolve around emitting warnings and errors. * They also may include pre-processor passes needed to do * error analysis more effectively. * * Clients that only want to analyze code (like IDEs) and not emit * code will only run checks and not optimizations. */ abstract protected List<PassFactory> getChecks(); /** * Gets the optimization passes to run. * * Optimization passes revolve around producing smaller and faster code. * They should always run after checking passes. */ abstract protected List<PassFactory> getOptimizations(); /** * Gets a graph of the passes run. For debugging. */ GraphvizGraph getPassGraph() { LinkedDirectedGraph<String, String> graph = LinkedDirectedGraph.createWithoutAnnotations(); Iterable<PassFactory> allPasses = Iterables.concat(getChecks(), getOptimizations()); String lastPass = null; String loopStart = null; for (PassFactory pass : allPasses) { String passName = pass.getName(); int i = 1; while (graph.hasNode(passName)) { passName = pass.getName

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>getName().equals(name)) { return i; } } throw new IllegalArgumentException( "No factory named '" + name + "' in the factory list"); } /** * Find the first pass provider that does not have a delegate. */ final PassConfig getBasePassConfig() { PassConfig current = this; while (current instanceof PassConfigDelegate) { current = ((PassConfigDelegate) current).delegate; } return current; } /** * Get intermediate state for a running pass config, so it can * be paused and started again later. */ protected abstract State getIntermediateState(); /** * Set the intermediate state for a pass config, to restart * a compilation process that had been previously paused. */ protected abstract void setIntermediateState(State state); /** * An implementation of PassConfig that just proxies all its method calls * into an inner class. */ static class PassConfigDelegate extends PassConfig { private final PassConfig delegate; PassConfigDelegate(PassConfig delegate) { super(delegate.options); this.delegate = delegate; } @Override protected List<PassFactory> getChecks() { return delegate.getChecks(); } @Override protected List<PassFactory> getOptimizations() { return delegate.getOptimizations(); } @Override MemoizedScopeCreator getTypedScopeCreator() { return delegate.getTypedScopeCreator(); } @Override Scope getTopScope() { return delegate.getTopScope(); } @Override protected State getIntermediateState() { return delegate.getIntermediateState(); } @Override protected void setIntermediateState(State state) { delegate.setIntermediateState(state); } } /** * Intermediate state for a running pass configuration. */ public static class State implements Serializable { private static final long serialVersionUID = 1L; final Map<String, Integer> cssNames; final Set<String> exportedNames; final CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator; final VariableMap variableMap; final VariableMap propertyMap; final VariableMap anonymousFunctionNameMap; final VariableMap stringMap; final FunctionNames functionNames; final String idGeneratorMap; public State(Map<String, Integer> cssNames, Set<String> exported

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS>.cloneTree(); for (int i = 1; i < argsToBind && params.getFirstChild() != null; i++) { if (params.getFirstChild().isVarArgs()) { break; } params.removeFirstChild(); } builder.withParamsNode(params); } } return builder.build(); } /** * Notice that "call" and "bind" have the same argument signature, * except that all the arguments of "bind" (except the first) * are optional. */ private FunctionType getCallOrBindSignature(boolean isCall) { boolean isBind = !isCall; FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(isCall ? getReturnType() : getBindReturnType(-1)) .withTemplateKeys(getTemplateKeys()); Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType( registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); if (isBind) { // The arguments of bind() are unique in that they are all // optional but not undefinable. for (Node current = thisTypeNode.getNext(); current != null; current = current.getNext()) { current.setOptionalArg(true); } } else if (isCall) { // The first argument of call() is optional iff all the arguments // are optional. It's sufficient to check the first argument. Node firstArg = thisTypeNode.getNext(); if (firstArg == null || firstArg.isOptionalArg() || firstArg.isVarArgs()) { thisTypeNode.setOptionalArg(true); } } builder.withParamsNode(params); } return builder.build(); } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if ("prototype".equals(name)) { ObjectType objType = type.toObjectType(); if (objType != null) { if (prototypeSlot != null && objType.isEquivalentTo(prototypeSlot.getType()))

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> { ((ObjectType) prototypeSlot.getType()).clearCachedValues(); } } } /** * Returns a list of types that are subtypes of this type. This is only valid * for constructor functions, and may be null. This allows a downward * traversal of the subtype graph. */ public List<FunctionType> getSubTypes() { return subTypes; } @Override public boolean hasCachedValues() { return prototypeSlot != null || super.hasCachedValues(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); call = (ArrowType) safeResolve(call, t, scope); if (prototypeSlot != null) { prototypeSlot.setType( safeResolve(prototypeSlot.getType(), t, scope)); } // Warning about typeOfThis if it doesn't resolve to an ObjectType // is handled further upstream. // // TODO(nicksantos): Handle this correctly if we have a UnionType. // // TODO(nicksantos): In ES3, the run-time coerces "null" to the global // activation object. In ES5, it leaves it as null. Just punt on this // issue for now by coercing out null. This is complicated by the // fact that when most people write @this {Foo}, they really don't // mean "nullable Foo". For certain tags (like @extends) we de-nullify // the name for them. JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope); if (maybeTypeOfThis != null) { maybeTypeOfThis = maybeTypeOfThis.restrictByNotNullOrUndefined(); } if (maybeTypeOfThis instanceof ObjectType) { typeOfThis = maybeTypeOfThis; } boolean changed = false; ImmutableList.Builder<ObjectType> resolvedInterfaces = ImmutableList.builder(); for (ObjectType iface : implementedInterfaces) { ObjectType resolvedIface = (ObjectType) iface.resolve(t, scope); resolvedInterfaces.add(resolvedIface); changed |= (resolvedIface != iface); } if (changed) { implementedInterfaces = resolvedInterfaces.build(); } if (subTypes != null) { for (int i = 0; i

Closure, 3

<FILEB>
<CHANGES>
if (c.canInline(t.getScope())) {
<CHANGEE>
<CHANGES>
private boolean canInline(final Scope scope) {
<CHANGEE>
<CHANGES>
case Token.NAME:
Var var = scope.getOwnSlot(input.getString());
if (var!= null
&& var.getParentNode().isCatch()) {
return true;
}
<CHANGEE>
<FILEE>
<FILEB> } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { <CHANGES> if (c.canInline()) { <CHANGEE> c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } <CHANGES> private boolean canInline() { <CHANGEE> // Cannot inline a parameter.<SCANS> < subTypes.size(); i++) { subTypes.set( i, JSType.toMaybeFunctionType(subTypes.get(i).resolve(t, scope))); } } return super.resolveInternal(t, scope); } @Override public String toDebugHashCodeString() { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return super.toDebugHashCodeString(); } StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getDebugHashCodeStringOf(typeOfThis)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); while (p != null) { b.append(", "); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); } } b.append(")"); b.append(": "); b.append(getDebugHashCodeStringOf(call.returnType)); return b.toString(); } private String getDebugHashCodeStringOf(JSType type) { if (type == this) { return "me"; } else { return type.toDebugHashCodeString(); } } /** Create a new constructor with the parameters and return type stripped. */ public FunctionType cloneWithoutArrowType() { FunctionType result = new FunctionType( registry, getReferenceName(), source, registry.createArrowType(null, null), getInstanceType(), null, true, false); result.setPrototypeBasedOn(getInstanceType()); return result; } @Override public boolean hasAnyTemplateTypesInternal() { return !getTemplateKeys().isEmpty() || typeOfThis.hasAnyTemplateTypes() || call.hasAnyTemplateTypes(); } }